@ohos-ports/slides-grab 1.5.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README-ko.md +292 -0
  3. package/README.md +301 -0
  4. package/bin/ohos-postinstall.cjs +118 -0
  5. package/bin/ppt-agent.js +620 -0
  6. package/convert.cjs +20 -0
  7. package/native/@img/sharp-libvips-openharmony-arm64/lib/glib-2.0/include/glibconfig.h +220 -0
  8. package/native/@img/sharp-libvips-openharmony-arm64/lib/index.js +1 -0
  9. package/native/@img/sharp-libvips-openharmony-arm64/lib/libexpat.so +0 -0
  10. package/native/@img/sharp-libvips-openharmony-arm64/lib/libffi.so +0 -0
  11. package/native/@img/sharp-libvips-openharmony-arm64/lib/libjpeg.so +0 -0
  12. package/native/@img/sharp-libvips-openharmony-arm64/lib/libpcre2-8.so.0 +0 -0
  13. package/native/@img/sharp-libvips-openharmony-arm64/lib/libvips-cpp.so.42.20.3 +0 -0
  14. package/native/@img/sharp-libvips-openharmony-arm64/lib/libvips.so.42.20.3 +0 -0
  15. package/native/@img/sharp-libvips-openharmony-arm64/package.json +13 -0
  16. package/native/@img/sharp-openharmony-arm64/lib/sharp-openharmony-arm64.node +0 -0
  17. package/native/@img/sharp-openharmony-arm64/package.json +16 -0
  18. package/package.json +95 -0
  19. package/runtimes/claude-code/agents/design-critic-agent.md +23 -0
  20. package/runtimes/codex/agents/slides-grab-design-critic.md +22 -0
  21. package/scripts/build-viewer.js +444 -0
  22. package/scripts/design-gate.js +258 -0
  23. package/scripts/download-video.js +213 -0
  24. package/scripts/editor-server.js +1167 -0
  25. package/scripts/figma-export.js +169 -0
  26. package/scripts/generate-image.js +116 -0
  27. package/scripts/generate-images.js +164 -0
  28. package/scripts/html2pdf.js +822 -0
  29. package/scripts/html2png.js +246 -0
  30. package/scripts/html2pptx.js +162 -0
  31. package/scripts/import-template.js +86 -0
  32. package/scripts/install-runtime.js +216 -0
  33. package/scripts/render-tldraw.js +44 -0
  34. package/scripts/validate-slides.js +221 -0
  35. package/skills/slides-grab/SKILL.md +65 -0
  36. package/skills/slides-grab/references/presentation-workflow-reference.md +61 -0
  37. package/skills/slides-grab-card-news/SKILL.md +35 -0
  38. package/skills/slides-grab-design/SKILL.md +88 -0
  39. package/skills/slides-grab-design/references/beautiful-slide-defaults.md +88 -0
  40. package/skills/slides-grab-design/references/design-gate.md +349 -0
  41. package/skills/slides-grab-design/references/design-rules.md +76 -0
  42. package/skills/slides-grab-design/references/design-system-full.md +568 -0
  43. package/skills/slides-grab-design/references/detailed-design-rules.md +70 -0
  44. package/skills/slides-grab-export/SKILL.md +55 -0
  45. package/skills/slides-grab-export/references/export-rules.md +27 -0
  46. package/skills/slides-grab-export/references/html2pptx.md +627 -0
  47. package/skills/slides-grab-export/references/ooxml.md +427 -0
  48. package/skills/slides-grab-export/references/pptx-skill-reference.md +189 -0
  49. package/skills/slides-grab-html/SKILL.md +67 -0
  50. package/skills/slides-grab-image/SKILL.md +82 -0
  51. package/skills/slides-grab-plan/SKILL.md +68 -0
  52. package/skills/slides-grab-plan/references/design-md-to-slides-conversion.md +135 -0
  53. package/skills/slides-grab-plan/references/outline-format.md +47 -0
  54. package/skills/slides-grab-plan/references/plan-workflow-reference.md +140 -0
  55. package/src/codex-imagen.js +182 -0
  56. package/src/design-diversity-data.js +6932 -0
  57. package/src/design-gate-report.js +244 -0
  58. package/src/design-gate-state.js +329 -0
  59. package/src/design-import.js +164 -0
  60. package/src/design-md-parser.js +415 -0
  61. package/src/design-styles-data.js +1928 -0
  62. package/src/design-styles.js +209 -0
  63. package/src/editor/codex-edit.js +584 -0
  64. package/src/editor/edit-subprocess.js +170 -0
  65. package/src/editor/editor-codex-prompt.md +50 -0
  66. package/src/editor/editor.html +1785 -0
  67. package/src/editor/js/editor-bbox.js +332 -0
  68. package/src/editor/js/editor-chat.js +56 -0
  69. package/src/editor/js/editor-direct-edit.js +238 -0
  70. package/src/editor/js/editor-dom.js +59 -0
  71. package/src/editor/js/editor-init.js +405 -0
  72. package/src/editor/js/editor-navigation.js +54 -0
  73. package/src/editor/js/editor-select.js +558 -0
  74. package/src/editor/js/editor-send.js +175 -0
  75. package/src/editor/js/editor-sse.js +163 -0
  76. package/src/editor/js/editor-state.js +41 -0
  77. package/src/editor/js/editor-type.js +71 -0
  78. package/src/editor/js/editor-utils.js +167 -0
  79. package/src/editor/js/model-registry.js +37 -0
  80. package/src/editor/screenshot.js +82 -0
  81. package/src/export-resolution.cjs +68 -0
  82. package/src/figma.js +71 -0
  83. package/src/html2pptx-scale.cjs +120 -0
  84. package/src/html2pptx.cjs +1262 -0
  85. package/src/image-contract.js +329 -0
  86. package/src/image-native.js +468 -0
  87. package/src/nano-banana.js +841 -0
  88. package/src/ohos-browser-bridge.cjs +272 -0
  89. package/src/pptx-raster-export.cjs +299 -0
  90. package/src/resolve.js +110 -0
  91. package/src/slide-mode.cjs +72 -0
  92. package/src/template-fidelity.js +267 -0
  93. package/src/template-import.js +505 -0
  94. package/src/template-layout.js +261 -0
  95. package/src/template-pack.js +255 -0
  96. package/src/tldraw/render.js +473 -0
  97. package/src/validation/cli.js +120 -0
  98. package/src/validation/core.js +1023 -0
  99. package/templates/chart.html +121 -0
  100. package/templates/closing.html +54 -0
  101. package/templates/content.html +50 -0
  102. package/templates/contents.html +60 -0
  103. package/templates/cover.html +64 -0
  104. package/templates/custom/.gitkeep +0 -0
  105. package/templates/custom/README.md +7 -0
  106. package/templates/design-styles/README.md +20 -0
  107. package/templates/design-styles/preview.html +4438 -0
  108. package/templates/diagram-tldraw.html +56 -0
  109. package/templates/diagram.html +98 -0
  110. package/templates/quote.html +31 -0
  111. package/templates/section-divider.html +43 -0
  112. package/templates/split-layout.html +43 -0
  113. package/templates/statistics.html +55 -0
  114. package/templates/team.html +49 -0
  115. package/templates/timeline.html +59 -0
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, readdirSync } from 'node:fs';
4
+ import { createRequire } from 'node:module';
5
+ import { resolve } from 'node:path';
6
+
7
+ import PptxGenJS from 'pptxgenjs';
8
+
9
+ import {
10
+ configureFigmaExportPresentation,
11
+ ensureOutputDirectory,
12
+ getFigmaImportCaveats,
13
+ normalizeFigmaOutput,
14
+ SLIDE_FILE_PATTERN,
15
+ sortFigmaSlideFiles,
16
+ } from '../src/figma.js';
17
+
18
+ const require = createRequire(import.meta.url);
19
+ const html2pptx = require('../src/html2pptx.cjs');
20
+ const { launchBrowser } = html2pptx;
21
+ const { DEFAULT_SLIDE_MODE, getSlideModeChoices, normalizeSlideMode } = require('../src/slide-mode.cjs');
22
+
23
+ const DEFAULT_SLIDES_DIR = 'slides';
24
+
25
+ function printUsage() {
26
+ process.stdout.write(
27
+ [
28
+ 'Usage: slides-grab figma [options]',
29
+ '',
30
+ 'Options:',
31
+ ` --slides-dir <path> Slide directory (default: ${DEFAULT_SLIDES_DIR})`,
32
+ ' --output <path> Output PPTX file (default: <slides-dir>-figma.pptx)',
33
+ ` --mode <mode> Slide mode: ${getSlideModeChoices().join('|')} (default: ${DEFAULT_SLIDE_MODE})`,
34
+ ' -h, --help Show this help message',
35
+ '',
36
+ 'Exports an experimental / unstable Figma Slides importable PPTX using the existing html2pptx pipeline.',
37
+ 'Treat both PPTX and Figma export as best-effort only.',
38
+ ].join('\n'),
39
+ );
40
+ process.stdout.write('\n');
41
+ }
42
+
43
+ function readOptionValue(args, index, optionName) {
44
+ const next = args[index + 1];
45
+ if (!next || next.startsWith('-')) {
46
+ throw new Error(`Missing value for ${optionName}.`);
47
+ }
48
+ return next;
49
+ }
50
+
51
+ function parseArgs(args) {
52
+ const options = {
53
+ slidesDir: DEFAULT_SLIDES_DIR,
54
+ output: '',
55
+ mode: DEFAULT_SLIDE_MODE,
56
+ help: false,
57
+ };
58
+
59
+ for (let i = 0; i < args.length; i += 1) {
60
+ const arg = args[i];
61
+ if (arg === '-h' || arg === '--help') {
62
+ options.help = true;
63
+ continue;
64
+ }
65
+
66
+ if (arg === '--slides-dir') {
67
+ options.slidesDir = readOptionValue(args, i, '--slides-dir');
68
+ i += 1;
69
+ continue;
70
+ }
71
+
72
+ if (arg.startsWith('--slides-dir=')) {
73
+ options.slidesDir = arg.slice('--slides-dir='.length);
74
+ continue;
75
+ }
76
+
77
+ if (arg === '--output') {
78
+ options.output = readOptionValue(args, i, '--output');
79
+ i += 1;
80
+ continue;
81
+ }
82
+
83
+ if (arg.startsWith('--output=')) {
84
+ options.output = arg.slice('--output='.length);
85
+ continue;
86
+ }
87
+
88
+ if (arg === '--mode') {
89
+ options.mode = normalizeSlideMode(readOptionValue(args, i, '--mode'));
90
+ i += 1;
91
+ continue;
92
+ }
93
+
94
+ if (arg.startsWith('--mode=')) {
95
+ options.mode = normalizeSlideMode(arg.slice('--mode='.length));
96
+ continue;
97
+ }
98
+
99
+ throw new Error(`Unknown option: ${arg}`);
100
+ }
101
+
102
+ if (typeof options.slidesDir !== 'string' || options.slidesDir.trim() === '') {
103
+ throw new Error('--slides-dir must be a non-empty string.');
104
+ }
105
+
106
+ options.slidesDir = options.slidesDir.trim();
107
+ options.mode = normalizeSlideMode(options.mode);
108
+ options.output = normalizeFigmaOutput(options.slidesDir, options.output);
109
+ return options;
110
+ }
111
+
112
+ function getHtmlSlides(slidesDir) {
113
+ if (!existsSync(slidesDir)) {
114
+ throw new Error(`Slides directory not found: ${slidesDir}`);
115
+ }
116
+
117
+ const files = readdirSync(slidesDir)
118
+ .filter((fileName) => SLIDE_FILE_PATTERN.test(fileName))
119
+ .sort(sortFigmaSlideFiles);
120
+
121
+ if (files.length === 0) {
122
+ throw new Error(`No slide-*.html files found in ${slidesDir}`);
123
+ }
124
+
125
+ return files;
126
+ }
127
+
128
+ async function main() {
129
+ const options = parseArgs(process.argv.slice(2));
130
+ if (options.help) {
131
+ printUsage();
132
+ return;
133
+ }
134
+
135
+ const slidesDir = resolve(process.cwd(), options.slidesDir);
136
+ const outputFile = resolve(process.cwd(), options.output);
137
+ const files = getHtmlSlides(slidesDir);
138
+
139
+ const pres = new PptxGenJS();
140
+ configureFigmaExportPresentation(pres, options.mode);
141
+
142
+ console.log(`Exporting ${files.length} slide(s) for Figma from ${slidesDir}`);
143
+
144
+ const browser = await launchBrowser(process.env.TMPDIR || '/tmp');
145
+ try {
146
+ for (const file of files) {
147
+ const filePath = resolve(slidesDir, file);
148
+ console.log(` Processing: ${file}`);
149
+ await html2pptx(filePath, pres, { browser });
150
+ }
151
+ } finally {
152
+ await browser.close();
153
+ }
154
+
155
+ await ensureOutputDirectory(outputFile);
156
+ await pres.writeFile({ fileName: outputFile });
157
+
158
+ console.log(`\nSaved Figma-importable PPTX: ${outputFile}`);
159
+ console.log('\nFigma import caveats:');
160
+ for (const caveat of getFigmaImportCaveats()) {
161
+ console.log(`- ${caveat}`);
162
+ }
163
+ console.log('\nManual import: Figma Slides -> Import -> select the generated .pptx file.');
164
+ }
165
+
166
+ main().catch((error) => {
167
+ console.error(`[slides-grab] ${error.message}`);
168
+ process.exit(1);
169
+ });
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ import {
7
+ DEFAULT_OPENAI_IMAGE_MODEL,
8
+ DEFAULT_OPENAI_IMAGE_BASE_URL,
9
+ DEFAULT_OPENAI_IMAGE_SIZE,
10
+ DEFAULT_CODEX_MODEL,
11
+ DEFAULT_IMAGE_PROVIDER,
12
+ DEFAULT_NANO_BANANA_ASPECT_RATIO,
13
+ DEFAULT_NANO_BANANA_IMAGE_SIZE,
14
+ DEFAULT_NANO_BANANA_MODEL,
15
+ IMAGE_PROVIDER_OPENAI,
16
+ IMAGE_PROVIDER_CODEX,
17
+ IMAGE_PROVIDER_NANO_BANANA,
18
+ buildOpenaiImageApiRequest,
19
+ buildOpenaiImageEndpoint,
20
+ buildNanoBananaApiRequest,
21
+ extractOpenaiGeneratedImage,
22
+ extractGeneratedImage,
23
+ generateOpenaiImage,
24
+ generateNanoBananaImage,
25
+ getOpenaiFallbackMessage,
26
+ getNanoBananaFallbackMessage,
27
+ getNanoBananaUsage,
28
+ normalizeImageProvider,
29
+ parseNanoBananaCliArgs,
30
+ resolveOpenaiApiKey,
31
+ resolveOpenaiBaseUrl,
32
+ resolveNanoBananaApiKey,
33
+ resolveNanoBananaOutputPath,
34
+ runNanoBananaCli,
35
+ saveNanoBananaImage,
36
+ } from '../src/nano-banana.js';
37
+ import {
38
+ CODEX_DEFAULT_MODEL,
39
+ CODEX_PROVIDER_AUTO,
40
+ CODEX_PROVIDER_CODEX_CLI,
41
+ CODEX_PROVIDER_PRIVATE_CODEX,
42
+ generateCodexImage,
43
+ getCodexFallbackMessage,
44
+ injectAspectRatioHint,
45
+ resolveCodexConfig,
46
+ } from '../src/codex-imagen.js';
47
+ import { writeImageNativeSlideWrapper } from '../src/image-native.js';
48
+
49
+ export {
50
+ DEFAULT_OPENAI_IMAGE_BASE_URL,
51
+ DEFAULT_OPENAI_IMAGE_MODEL,
52
+ DEFAULT_OPENAI_IMAGE_SIZE,
53
+ DEFAULT_CODEX_MODEL,
54
+ DEFAULT_IMAGE_PROVIDER,
55
+ DEFAULT_NANO_BANANA_ASPECT_RATIO,
56
+ DEFAULT_NANO_BANANA_IMAGE_SIZE,
57
+ DEFAULT_NANO_BANANA_MODEL,
58
+ CODEX_DEFAULT_MODEL,
59
+ CODEX_PROVIDER_AUTO,
60
+ CODEX_PROVIDER_CODEX_CLI,
61
+ CODEX_PROVIDER_PRIVATE_CODEX,
62
+ IMAGE_PROVIDER_OPENAI,
63
+ IMAGE_PROVIDER_CODEX,
64
+ IMAGE_PROVIDER_NANO_BANANA,
65
+ buildOpenaiImageApiRequest,
66
+ buildOpenaiImageEndpoint,
67
+ buildNanoBananaApiRequest,
68
+ extractOpenaiGeneratedImage,
69
+ extractGeneratedImage,
70
+ generateOpenaiImage,
71
+ generateCodexImage,
72
+ generateNanoBananaImage,
73
+ getOpenaiFallbackMessage,
74
+ getCodexFallbackMessage,
75
+ getNanoBananaFallbackMessage,
76
+ getNanoBananaUsage,
77
+ injectAspectRatioHint,
78
+ normalizeImageProvider,
79
+ parseNanoBananaCliArgs,
80
+ resolveOpenaiApiKey,
81
+ resolveCodexConfig,
82
+ resolveOpenaiBaseUrl,
83
+ resolveNanoBananaApiKey,
84
+ resolveNanoBananaOutputPath,
85
+ runNanoBananaCli,
86
+ saveNanoBananaImage,
87
+ };
88
+
89
+ export async function main(argv = process.argv.slice(2), options = {}) {
90
+ const imageNative = argv.includes('--image-native');
91
+ const forwardedArgs = argv.filter((arg) => arg !== '--image-native');
92
+ const parsed = parseNanoBananaCliArgs(forwardedArgs);
93
+ const target = await runNanoBananaCli(forwardedArgs, options);
94
+ if (!imageNative || parsed.help || !target) return target;
95
+
96
+ const wrapper = await writeImageNativeSlideWrapper({
97
+ slidesDir: parsed.slidesDir,
98
+ slideName: parsed.name,
99
+ assetRef: target.relativeRef,
100
+ prompt: parsed.prompt,
101
+ provider: target.provider,
102
+ model: target.model,
103
+ title: parsed.name,
104
+ });
105
+ (options.stdout || process.stdout).write(`Created image-native slide wrapper: ${wrapper.slideFile}\n`);
106
+ return { ...target, imageNative: wrapper };
107
+ }
108
+
109
+ const isMain = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
110
+
111
+ if (isMain) {
112
+ main().catch((error) => {
113
+ console.error(`[slides-grab] ${error.message}`);
114
+ process.exit(1);
115
+ });
116
+ }
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ import {
7
+ buildImageNativePrompt,
8
+ generateImageNativeSlides,
9
+ parseImageNativeOutline,
10
+ } from '../src/image-native.js';
11
+ import { IMAGE_PROVIDER_CODEX } from '../src/nano-banana.js';
12
+
13
+ export {
14
+ buildImageNativePrompt,
15
+ generateImageNativeSlides,
16
+ parseImageNativeOutline,
17
+ };
18
+
19
+ function readOptionValue(argv, index, option) {
20
+ const value = argv[index + 1];
21
+ if (!value || value.startsWith('--')) throw new Error(`${option} is missing a value.`);
22
+ return value;
23
+ }
24
+
25
+ export function parseGenerateImagesCliArgs(argv) {
26
+ const parsed = {
27
+ outline: '',
28
+ slidesDir: 'slides',
29
+ templatePack: '',
30
+ provider: IMAGE_PROVIDER_CODEX,
31
+ model: '',
32
+ baseUrl: '',
33
+ help: false,
34
+ };
35
+
36
+ const args = Array.isArray(argv) ? [...argv] : [];
37
+ for (let i = 0; i < args.length; i += 1) {
38
+ const arg = args[i];
39
+ if (arg === '--help' || arg === '-h') {
40
+ parsed.help = true;
41
+ continue;
42
+ }
43
+ if (arg === '--outline') {
44
+ parsed.outline = readOptionValue(args, i, '--outline');
45
+ i += 1;
46
+ continue;
47
+ }
48
+ if (arg.startsWith('--outline=')) {
49
+ parsed.outline = arg.slice('--outline='.length);
50
+ continue;
51
+ }
52
+ if (arg === '--slides-dir') {
53
+ parsed.slidesDir = readOptionValue(args, i, '--slides-dir');
54
+ i += 1;
55
+ continue;
56
+ }
57
+ if (arg.startsWith('--slides-dir=')) {
58
+ parsed.slidesDir = arg.slice('--slides-dir='.length);
59
+ continue;
60
+ }
61
+ if (arg === '--template-pack') {
62
+ parsed.templatePack = readOptionValue(args, i, '--template-pack');
63
+ i += 1;
64
+ continue;
65
+ }
66
+ if (arg.startsWith('--template-pack=')) {
67
+ parsed.templatePack = arg.slice('--template-pack='.length);
68
+ continue;
69
+ }
70
+ if (arg === '--provider') {
71
+ parsed.provider = readOptionValue(args, i, '--provider');
72
+ i += 1;
73
+ continue;
74
+ }
75
+ if (arg.startsWith('--provider=')) {
76
+ parsed.provider = arg.slice('--provider='.length);
77
+ continue;
78
+ }
79
+ if (arg === '--model') {
80
+ parsed.model = readOptionValue(args, i, '--model');
81
+ i += 1;
82
+ continue;
83
+ }
84
+ if (arg.startsWith('--model=')) {
85
+ parsed.model = arg.slice('--model='.length);
86
+ continue;
87
+ }
88
+ if (arg === '--base-url') {
89
+ parsed.baseUrl = readOptionValue(args, i, '--base-url');
90
+ i += 1;
91
+ continue;
92
+ }
93
+ if (arg.startsWith('--base-url=')) {
94
+ parsed.baseUrl = arg.slice('--base-url='.length);
95
+ continue;
96
+ }
97
+ throw new Error(`Unknown option: ${arg}`);
98
+ }
99
+
100
+ if (parsed.help) return parsed;
101
+ for (const key of ['outline', 'slidesDir', 'provider']) {
102
+ if (typeof parsed[key] !== 'string' || parsed[key].trim() === '') {
103
+ throw new Error(`--${key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)} must be a non-empty string.`);
104
+ }
105
+ parsed[key] = parsed[key].trim();
106
+ }
107
+ parsed.templatePack = parsed.templatePack.trim();
108
+ parsed.model = parsed.model.trim();
109
+ parsed.baseUrl = parsed.baseUrl.trim();
110
+ return parsed;
111
+ }
112
+
113
+ export function getGenerateImagesUsage() {
114
+ return [
115
+ 'Usage: slides-grab generate-images --outline <path> [options]',
116
+ '',
117
+ 'Generate image-native wrapper slides from a slide outline and optional template pack context.',
118
+ '',
119
+ 'Options:',
120
+ ' --outline <path> Markdown slide outline to convert',
121
+ ' --slides-dir <path> Slide directory (default: slides)',
122
+ ' --template-pack <path> Optional template-pack.json path; defaults to <slides-dir>/.slides-grab/template-pack.json when present',
123
+ ' --provider <name> codex (default), openai, or nano-banana',
124
+ ' --model <id> Optional provider model override',
125
+ ' --base-url <url> OpenAI-compatible base URL for provider=openai',
126
+ ' -h, --help Show this help text',
127
+ '',
128
+ 'Default provider: codex (uses your local Codex ChatGPT login — no API key required).',
129
+ ].join('\n');
130
+ }
131
+
132
+ export async function main(argv = process.argv.slice(2), options = {}) {
133
+ const parsed = parseGenerateImagesCliArgs(argv);
134
+ const stdout = options.stdout || process.stdout;
135
+ if (parsed.help) {
136
+ stdout.write(`${getGenerateImagesUsage()}\n`);
137
+ return null;
138
+ }
139
+ const result = await generateImageNativeSlides({
140
+ outlinePath: parsed.outline,
141
+ slidesDir: parsed.slidesDir,
142
+ templatePackPath: parsed.templatePack,
143
+ provider: parsed.provider,
144
+ model: parsed.model,
145
+ baseUrl: parsed.baseUrl,
146
+ env: options.env || process.env,
147
+ fetchImpl: options.fetchImpl || globalThis.fetch,
148
+ generateImageImpl: options.generateImageImpl,
149
+ });
150
+ stdout.write(`Generated image-native slides: ${result.slides.length}\n`);
151
+ for (const slide of result.slides) {
152
+ stdout.write(`- ${slide.slideFile} -> ${slide.assetRef} (${slide.provider})\n`);
153
+ }
154
+ return result;
155
+ }
156
+
157
+ const isMain = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
158
+
159
+ if (isMain) {
160
+ main().catch((error) => {
161
+ console.error(`[slides-grab] ${error.message}`);
162
+ process.exit(1);
163
+ });
164
+ }