@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.
- package/LICENSE +21 -0
- package/README-ko.md +292 -0
- package/README.md +301 -0
- package/bin/ohos-postinstall.cjs +118 -0
- package/bin/ppt-agent.js +620 -0
- package/convert.cjs +20 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/glib-2.0/include/glibconfig.h +220 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/index.js +1 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libexpat.so +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libffi.so +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libjpeg.so +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libpcre2-8.so.0 +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libvips-cpp.so.42.20.3 +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libvips.so.42.20.3 +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/package.json +13 -0
- package/native/@img/sharp-openharmony-arm64/lib/sharp-openharmony-arm64.node +0 -0
- package/native/@img/sharp-openharmony-arm64/package.json +16 -0
- package/package.json +95 -0
- 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 +444 -0
- package/scripts/design-gate.js +258 -0
- package/scripts/download-video.js +213 -0
- package/scripts/editor-server.js +1167 -0
- package/scripts/figma-export.js +169 -0
- package/scripts/generate-image.js +116 -0
- package/scripts/generate-images.js +164 -0
- package/scripts/html2pdf.js +822 -0
- package/scripts/html2png.js +246 -0
- package/scripts/html2pptx.js +162 -0
- package/scripts/import-template.js +86 -0
- package/scripts/install-runtime.js +216 -0
- package/scripts/render-tldraw.js +44 -0
- package/scripts/validate-slides.js +221 -0
- package/skills/slides-grab/SKILL.md +65 -0
- package/skills/slides-grab/references/presentation-workflow-reference.md +61 -0
- package/skills/slides-grab-card-news/SKILL.md +35 -0
- package/skills/slides-grab-design/SKILL.md +88 -0
- package/skills/slides-grab-design/references/beautiful-slide-defaults.md +88 -0
- package/skills/slides-grab-design/references/design-gate.md +349 -0
- package/skills/slides-grab-design/references/design-rules.md +76 -0
- package/skills/slides-grab-design/references/design-system-full.md +568 -0
- package/skills/slides-grab-design/references/detailed-design-rules.md +70 -0
- package/skills/slides-grab-export/SKILL.md +55 -0
- package/skills/slides-grab-export/references/export-rules.md +27 -0
- package/skills/slides-grab-export/references/html2pptx.md +627 -0
- package/skills/slides-grab-export/references/ooxml.md +427 -0
- package/skills/slides-grab-export/references/pptx-skill-reference.md +189 -0
- package/skills/slides-grab-html/SKILL.md +67 -0
- package/skills/slides-grab-image/SKILL.md +82 -0
- package/skills/slides-grab-plan/SKILL.md +68 -0
- package/skills/slides-grab-plan/references/design-md-to-slides-conversion.md +135 -0
- package/skills/slides-grab-plan/references/outline-format.md +47 -0
- package/skills/slides-grab-plan/references/plan-workflow-reference.md +140 -0
- package/src/codex-imagen.js +182 -0
- package/src/design-diversity-data.js +6932 -0
- package/src/design-gate-report.js +244 -0
- package/src/design-gate-state.js +329 -0
- package/src/design-import.js +164 -0
- package/src/design-md-parser.js +415 -0
- package/src/design-styles-data.js +1928 -0
- package/src/design-styles.js +209 -0
- package/src/editor/codex-edit.js +584 -0
- package/src/editor/edit-subprocess.js +170 -0
- package/src/editor/editor-codex-prompt.md +50 -0
- package/src/editor/editor.html +1785 -0
- package/src/editor/js/editor-bbox.js +332 -0
- package/src/editor/js/editor-chat.js +56 -0
- package/src/editor/js/editor-direct-edit.js +238 -0
- package/src/editor/js/editor-dom.js +59 -0
- package/src/editor/js/editor-init.js +405 -0
- package/src/editor/js/editor-navigation.js +54 -0
- package/src/editor/js/editor-select.js +558 -0
- package/src/editor/js/editor-send.js +175 -0
- package/src/editor/js/editor-sse.js +163 -0
- package/src/editor/js/editor-state.js +41 -0
- package/src/editor/js/editor-type.js +71 -0
- package/src/editor/js/editor-utils.js +167 -0
- package/src/editor/js/model-registry.js +37 -0
- package/src/editor/screenshot.js +82 -0
- package/src/export-resolution.cjs +68 -0
- package/src/figma.js +71 -0
- package/src/html2pptx-scale.cjs +120 -0
- package/src/html2pptx.cjs +1262 -0
- package/src/image-contract.js +329 -0
- package/src/image-native.js +468 -0
- package/src/nano-banana.js +841 -0
- package/src/ohos-browser-bridge.cjs +272 -0
- package/src/pptx-raster-export.cjs +299 -0
- package/src/resolve.js +110 -0
- package/src/slide-mode.cjs +72 -0
- package/src/template-fidelity.js +267 -0
- package/src/template-import.js +505 -0
- package/src/template-layout.js +261 -0
- package/src/template-pack.js +255 -0
- package/src/tldraw/render.js +473 -0
- package/src/validation/cli.js +120 -0
- package/src/validation/core.js +1023 -0
- package/templates/chart.html +121 -0
- package/templates/closing.html +54 -0
- package/templates/content.html +50 -0
- package/templates/contents.html +60 -0
- package/templates/cover.html +64 -0
- package/templates/custom/.gitkeep +0 -0
- package/templates/custom/README.md +7 -0
- package/templates/design-styles/README.md +20 -0
- package/templates/design-styles/preview.html +4438 -0
- package/templates/diagram-tldraw.html +56 -0
- package/templates/diagram.html +98 -0
- package/templates/quote.html +31 -0
- package/templates/section-divider.html +43 -0
- package/templates/split-layout.html +43 -0
- package/templates/statistics.html +55 -0
- package/templates/team.html +49 -0
- package/templates/timeline.html +59 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: slides-grab-image
|
|
3
|
+
description: Image-native presentation pipeline usable in Codex and Claude Code. Generate whole-slide raster images one slide at a time with slides-grab image, passing the reference template page as --reference so the model copies the layout and style and only swaps the content. Use when visual fidelity to an existing template matters more than editable text.
|
|
4
|
+
metadata:
|
|
5
|
+
short-description: Image-native pipeline — per-slide raster generation with template reference
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# slides-grab Image Skill
|
|
9
|
+
|
|
10
|
+
Generate whole-slide raster images where the generated PNG **is** the slide. Each slide is generated individually with `slides-grab image`, passing the matching reference template page image(s) via `--reference` so the model reproduces the template's layout, colors, fonts, and density — only swapping in the new slide's content. Use this pipeline when the user wants to match an existing corporate template, filled deck, or brand form visually.
|
|
11
|
+
|
|
12
|
+
## Mode signal
|
|
13
|
+
The plan stage records `mode: image-native` in `slide-outline.md`. The user typically provides a reference template (PPTX, PDF, HTML examples) and says "make slides in this form."
|
|
14
|
+
|
|
15
|
+
## Core rule: reference-guided per-slide generation
|
|
16
|
+
- **Generate one slide at a time** with `slides-grab image`. Do NOT use `slides-grab generate-images` (the batch command auto-assembles garbage prompts from markdown and produces inconsistent output).
|
|
17
|
+
- **Always pass at least one reference template page image** via `--reference <path>` (repeatable). The reference page(s) must be the same slide-type (cover, content, closing) from the source template. The model must copy the reference's layout, color bands, typography hierarchy, whitespace, and density — only the text content and any embedded photos change.
|
|
18
|
+
- The prompt must explicitly instruct: *"Follow the reference image's layout, composition, color palette, and typography hierarchy exactly. Keep the same band positions, spacing, and visual rhythm. Only replace the text content and photos with the new slide's content below."*
|
|
19
|
+
- Pass any real photos/assets to embed as additional `--reference` images so the model can place them into the template layout.
|
|
20
|
+
|
|
21
|
+
## Prompt structure (use this pattern)
|
|
22
|
+
```
|
|
23
|
+
slides-grab image \
|
|
24
|
+
--prompt "Follow the reference image layout exactly — same color bands, same typography hierarchy, same spacing and density. This is a [cover/content/closing] slide. Replace the text with: Title: '<new title>'. Body: '<new body text>'. Bullets: <bullet list>. Keep the template's visual style, fonts, and color palette. Render all text sharp and legible. 16:9, 720pt × 405pt." \
|
|
25
|
+
--slides-dir <path> \
|
|
26
|
+
--reference <template-page-cover.png> \
|
|
27
|
+
--reference <real-photo-to-embed.png>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Key prompt principles:
|
|
31
|
+
- **Describe the content to inject**, not the visual design — the reference image defines the design.
|
|
32
|
+
- Name the slide type (cover, content, closing, stat) so the model picks the right reference density.
|
|
33
|
+
- List the exact title, body, and bullets as literal strings the model should render.
|
|
34
|
+
- If embedding real photos, name them in the prompt ("place photo-1 in the left grid cell") and pass them as `--reference`.
|
|
35
|
+
- Require sharp, legible, correctly spelled text.
|
|
36
|
+
|
|
37
|
+
## Workflow
|
|
38
|
+
|
|
39
|
+
### Stage 1 — Plan (template-driven dense outline)
|
|
40
|
+
Use the installed **slides-grab-plan** skill with image-native outline density rules.
|
|
41
|
+
1. Take topic, audience, and tone.
|
|
42
|
+
2. If the user provides a reference template/PDF/PPTX, import it with `slides-grab import-template --input <path>`. Also render the template pages to PNG (e.g. `pdftoppm -png -r 150 <input>.pdf <prefix>`) and store under `<slides-dir>/.slides-grab/template-previews/`. These page renders are the `--reference` images for Stage 2.
|
|
43
|
+
3. **Read the template pages in order** (page-01, page-02, ...). For each page, identify its layout type and information density — how many text blocks, data points, bullets, captions, and visual elements it carries. This is the critical step: the outline must match the template's per-slide density, not write a thin HTML-style summary.
|
|
44
|
+
4. **Write a dense outline**: one outline slide per template page, carrying the full content the user wants — titles, subtitles, body paragraphs, full bullet lists, stat values, table data, captions, and image placement notes — at the volume the template page demonstrates. Image-native slides are single rasters with no HTML structure limits, so they hold more text and data per slide than semantic HTML. Do not under-fill.
|
|
45
|
+
5. Tag each outline slide with `template-page: NN` so Stage 2 knows which reference image to pass.
|
|
46
|
+
6. Record `mode: image-native` and `style: template-pack` in `slide-outline.md` meta.
|
|
47
|
+
7. Present outline, revise until approved.
|
|
48
|
+
|
|
49
|
+
### Stage 2 — Design (per-slide image generation)
|
|
50
|
+
1. Read approved `slide-outline.md`. For each slide, read its `template-page: NN` tag and pick that exact template page render from `.slides-grab/template-previews/page-NN.png` as the primary `--reference`. If the slide has no tag, pick 1–2 layout-similar pages by slide type (cover, content, stat, closing).
|
|
51
|
+
2. For each slide, run `slides-grab image` with:
|
|
52
|
+
- `--prompt` containing ALL the content from the outline for that slide — the full title, subtitle, body paragraphs, every bullet, every stat value, every caption — not a summary. Precede the content with "Follow the reference image layout exactly — same color bands, same typography hierarchy, same spacing and density. Only replace the text content with the content below."
|
|
53
|
+
- `--reference` pointing to the matching template page render(s)
|
|
54
|
+
- `--reference` for any real photos to embed (member photos, product shots, logos)
|
|
55
|
+
- `--slides-dir <path>` and `--name slide-XX` (so the output is `assets/slide-XX.png`)
|
|
56
|
+
- `--image-native` so the command also writes the editor-compatible `slide-XX.html` wrapper and `.slides-grab/image-native/slide-XX.json` regeneration metadata
|
|
57
|
+
- `--provider codex` (default; reuses `~/.codex/auth.json`, run `codex login` once, no API key). Alternatives: `--provider openai` (OPENAI_API_KEY, gpt-image-2), `--provider nano-banana` (GOOGLE_API_KEY/GEMINI_API_KEY).
|
|
58
|
+
3. Confirm each command reports `Created image-native slide wrapper: slide-XX.html`. Do not hand-write the wrapper; `--image-native` keeps the PNG, wrapper, and regeneration metadata contract aligned for `slides-grab edit-image`.
|
|
59
|
+
4. Run `slides-grab validate --slides-dir <path>`. Auto-fix failures until it passes.
|
|
60
|
+
5. Run the design gate (`../slides-grab-design/references/design-gate.md`): capture PNG evidence, run Pass A + Pass B, synthesize verdict. Resolve all Critical findings, re-generate the failing slide, re-render, re-review until `Proceed`. Record with `slides-grab design-gate --slides-dir <path> --verdict proceed --pass-a-report <a.md> --pass-b-report <b.md>`.
|
|
61
|
+
6. For revisions, regenerate the specific slide with `slides-grab image` (same `--reference`, adjusted `--prompt`), or launch the image-native editor with `slides-grab edit-image --slides-dir <path>`.
|
|
62
|
+
|
|
63
|
+
### Stage 3 — Export
|
|
64
|
+
Use the installed **slides-grab-export** skill. Requires a fresh `Proceed` gate receipt.
|
|
65
|
+
1. Widescreen → `slides-grab pdf --slides-dir <path> --output <name>.pdf`.
|
|
66
|
+
2. Per-slide PNG → `slides-grab png --slides-dir <path> --output-dir <path>/out-png --resolution 2160p`.
|
|
67
|
+
3. PPTX/Figma (experimental / unstable) → `slides-grab convert` / `slides-grab figma`.
|
|
68
|
+
|
|
69
|
+
## Rules
|
|
70
|
+
- Keep slide size 720pt × 405pt (16:9).
|
|
71
|
+
- **Always pass `--reference` template page images** — never generate image-native slides from a text-only prompt. The reference defines the visual design; the prompt defines the content.
|
|
72
|
+
- Generate one slide at a time with `slides-grab image`. Do not use `slides-grab generate-images`.
|
|
73
|
+
- `slides-grab image --image-native --name slide-XX` writes the wrapper and regeneration metadata. Do not hand-write semantic text into wrapper slides.
|
|
74
|
+
- Put generated assets under `<slides-dir>/assets/`, reference as `./assets/<file>`.
|
|
75
|
+
- Allow `data:` URLs when a slide must be self-contained; never leave remote `http(s)://` image URLs in saved HTML.
|
|
76
|
+
- Do not present slides for review until `slides-grab validate` passes.
|
|
77
|
+
- Do not advance to export while any Critical design-gate finding is unresolved.
|
|
78
|
+
|
|
79
|
+
## Reference
|
|
80
|
+
- `../slides-grab-plan/SKILL.md` and `../slides-grab-plan/references/`
|
|
81
|
+
- `../slides-grab-design/SKILL.md` and `../slides-grab-design/references/`
|
|
82
|
+
- `../slides-grab-export/SKILL.md` and `../slides-grab-export/references/`
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: slides-grab-plan
|
|
3
|
+
description: Stage 1 planning skill usable in Codex and Claude Code. Build and iterate slide-outline.md until explicit user approval.
|
|
4
|
+
metadata:
|
|
5
|
+
short-description: Create and revise slide outline before design stage
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# slides-grab Plan Skill
|
|
9
|
+
|
|
10
|
+
Use this when the user asks to start a new presentation from scratch.
|
|
11
|
+
|
|
12
|
+
## Goal
|
|
13
|
+
Produce an approved `slide-outline.md` before any slide HTML generation.
|
|
14
|
+
|
|
15
|
+
## Inputs
|
|
16
|
+
- Topic and intent
|
|
17
|
+
- Audience
|
|
18
|
+
- Tone and constraints
|
|
19
|
+
- Optional research findings
|
|
20
|
+
|
|
21
|
+
## Output
|
|
22
|
+
- `slide-outline.md` (must include `style: <id>`, `style: template-pack`, or an approved custom style direction in meta section)
|
|
23
|
+
- For chart-heavy decks, explicit chart slide notes: chart type, data source, key comparison, and whether the design stage should use the default Chart.js canvas template.
|
|
24
|
+
|
|
25
|
+
## Workflow
|
|
26
|
+
1. Analyze user goal and audience.
|
|
27
|
+
2. **Style selection (mandatory, before outline):** Four paths are accepted, in priority order:
|
|
28
|
+
a. **Imported template pack** — when the user provides a corporate deck, reference slides, HTML examples, or brand images, run `slides-grab import-template --input <path> --slides-dir <slides-dir>` (repeat `--input` for multiple examples). Prefer filled representative decks/examples over empty master templates: filled examples reveal density, schema field limits, font resolution, and layout stress cases; an empty master alone is insufficient. Confirm the generated `<slides-dir>/.slides-grab/template-pack.json` is the intended reference source. Record as `style: template-pack`.
|
|
29
|
+
b. **Bundled style** — run `slides-grab list-styles`, shortlist 2–3 styles, and get explicit user approval. Optionally offer `slides-grab preview-styles` for visual preview. Record as `style: <id>`.
|
|
30
|
+
c. **Custom DESIGN.md path** — if a local `DESIGN.md` exists (e.g. provided directly or fetched via `slides-grab import-design <https-url>`), inspect it with `slides-grab show-design ./DESIGN.md` and confirm with the user.
|
|
31
|
+
d. **Free-form custom direction** — if neither imported template pack, bundled style, nor DESIGN.md fits, propose a written custom direction and get approval.
|
|
32
|
+
3. **DESIGN.md → DESIGN.slides.md conversion (mandatory when path 2c was chosen):**
|
|
33
|
+
- A `DESIGN.md` imported from `voltagent/awesome-design-md` or similar sources describes a **marketing website** (top-nav, hero-band, CTA buttons, pricing cards, footer-band). Slides are **single 720pt × 405pt frames** with no scroll, no nav, no clicks — copying web components into slides produces deck pages that look like landing pages.
|
|
34
|
+
- Read `references/design-md-to-slides-conversion.md` for the canonical conversion guide.
|
|
35
|
+
- Translate the imported `./DESIGN.md` into a sibling `./DESIGN.slides.md` next to it. Leave the original `DESIGN.md` untouched. The `DESIGN.slides.md` MUST follow the Output Contract in the reference and apply every row of the web → slide mapping table (top-nav → eyebrow strip, hero-band → cover layout, CTA buttons → kicker text, footer-band → thin footer strip, pricing grids → dropped, etc.).
|
|
36
|
+
- Present a 5–10 line summary of the conversion to the user (kept tokens + dropped web sections + new slide layouts inferred) and wait for explicit approval before continuing.
|
|
37
|
+
- After approval, run `slides-grab show-design ./DESIGN.slides.md` to confirm the parser reads it cleanly.
|
|
38
|
+
4. Create or revise `slide-outline.md` with ordered slides and key messages. Record the approved style reference in the meta section:
|
|
39
|
+
- bundled style → `style: <id>`
|
|
40
|
+
- converted DESIGN.slides.md → `style: ./DESIGN.slides.md`
|
|
41
|
+
- imported template pack → `style: template-pack` and keep `<slides-dir>/.slides-grab/template-pack.json` with its `.slides-grab/template-previews/` assets
|
|
42
|
+
- free-form custom direction → leave a one-paragraph `style:` block describing it
|
|
43
|
+
- for chart slides, name the intended chart type and data payload in the slide notes so Stage 2 can build a real Chart.js canvas instead of placeholder bars or decorative pseudo-charts
|
|
44
|
+
- delivery mode → `mode: html` for semantic slide HTML, or `mode: image-native` for raster wrapper slides generated from the approved template/style; prefer `image-native` for existing corporate templates/forms when the user asks for visual matching rather than editable HTML
|
|
45
|
+
4b. **Image-native outline density (mandatory when mode: image-native and a template is provided):** When the user provides a reference template (PDF/PPTX) for image-native generation, do NOT write a thin HTML-style outline. Instead:
|
|
46
|
+
- Render the template pages to PNG and open them in order (page-01, page-02, ...).
|
|
47
|
+
- For each template page, identify its layout type (cover, section divider, content grid, stat tiles, table, timeline, closing) and measure its **information density** — how many distinct text blocks, data points, bullets, captions, and visual elements it carries.
|
|
48
|
+
- Write one outline slide per template page (or per logical template section), matching that page's layout type and information volume. If template page 3 has a 2×3 stat grid with 6 data points and 3 captions, the corresponding outline slide must also list 6 data points and 3 captions — not just a one-line summary.
|
|
49
|
+
- Image-native slides are single raster images with no HTML structure constraints, so they can hold **more text and data per slide** than semantic HTML. Pack the outline with the full content the user wants on each slide — titles, subtitles, body paragraphs, bullet lists, data tables, stat values, captions, and image placement notes — at the density the template page demonstrates.
|
|
50
|
+
- Tag each outline slide with its matching template page number (e.g. `template-page: 03`) so Stage 2 knows which `--reference` image to pass.
|
|
51
|
+
- The result should be a noticeably denser outline than HTML mode: more bullets, more data, more text per slide, matching what the template's filled pages actually carry.
|
|
52
|
+
5. Present a concise summary to user.
|
|
53
|
+
6. Repeat revisions until explicit approval.
|
|
54
|
+
|
|
55
|
+
## Rules
|
|
56
|
+
- **Do not write the outline before the user approves a style.** Style selection comes first.
|
|
57
|
+
- Do not generate slide HTML (`<slides-dir>/slide-*.html`) in this stage.
|
|
58
|
+
- Keep scope to structure, narrative, and style selection.
|
|
59
|
+
- For quantitative stories, plan charts as evidence: identify the source values, comparison axis, baseline, and intended takeaway. Do not invent filler metrics just to fill a chart.
|
|
60
|
+
- Ask for approval before moving to design.
|
|
61
|
+
- Assume later stages run through the packaged `slides-grab` CLI.
|
|
62
|
+
- Use the packaged CLI and bundled references only; do not depend on unpublished agent-specific files.
|
|
63
|
+
|
|
64
|
+
## Reference
|
|
65
|
+
If needed, use the bundled outline reference:
|
|
66
|
+
- `references/outline-format.md`
|
|
67
|
+
- `references/plan-workflow-reference.md` — archived detailed planning workflow and runtime-neutral outline-worker guidance
|
|
68
|
+
- `references/design-md-to-slides-conversion.md` — DESIGN.md (web) → DESIGN.slides.md (slide) translation guide, including the structured output template and the web → slide mapping table
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# DESIGN.md → DESIGN.slides.md Conversion
|
|
2
|
+
|
|
3
|
+
Web-flavored `DESIGN.md` files (Google Stitch / voltagent/awesome-design-md
|
|
4
|
+
convention) describe **marketing websites**: top-nav, hero-band, CTA buttons,
|
|
5
|
+
pricing cards, footer-band, multi-section scrolling pages.
|
|
6
|
+
|
|
7
|
+
A slide is a **720pt × 405pt single frame, no scroll, no clicks, no nav**.
|
|
8
|
+
Pasting a web design system into slides produces deck pages that look like
|
|
9
|
+
landing pages — wrong slot.
|
|
10
|
+
|
|
11
|
+
This document is the canonical reference the agent uses to translate a
|
|
12
|
+
DESIGN.md into a sibling `DESIGN.slides.md` that fits the slide medium.
|
|
13
|
+
|
|
14
|
+
## Output contract
|
|
15
|
+
|
|
16
|
+
The agent MUST produce a file named `DESIGN.slides.md` next to the source
|
|
17
|
+
`DESIGN.md` at the deck root. The original `DESIGN.md` MUST be left untouched.
|
|
18
|
+
|
|
19
|
+
`DESIGN.slides.md` MUST contain the following sections, in this order:
|
|
20
|
+
|
|
21
|
+
```markdown
|
|
22
|
+
---
|
|
23
|
+
version: alpha
|
|
24
|
+
name: <inherited from DESIGN.md `name`>
|
|
25
|
+
description: <one-sentence slide-flavored mood — not a marketing paragraph>
|
|
26
|
+
derived-from: <relative path to source DESIGN.md, e.g. ./DESIGN.md>
|
|
27
|
+
medium: slides-16x9
|
|
28
|
+
colors:
|
|
29
|
+
<token-name>: <#hex>
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Overview
|
|
33
|
+
One short paragraph describing the slide deck's atmosphere — material, energy,
|
|
34
|
+
typographic voice. No mention of pages, scroll, nav, CTA, pricing, or routes.
|
|
35
|
+
|
|
36
|
+
## Background
|
|
37
|
+
Solid fills only. List 1-3 canvas modes that alternate across slides.
|
|
38
|
+
|
|
39
|
+
## Colors
|
|
40
|
+
Markdown table of the slide-active palette. Drop any role that exists only
|
|
41
|
+
for hover/focus/disabled states (those don't apply to static slides).
|
|
42
|
+
|
|
43
|
+
## Typography
|
|
44
|
+
Two faces: a display face for headlines and a body face for everything else.
|
|
45
|
+
Optionally a mono face for eyebrows / IDs / code motifs. Include working
|
|
46
|
+
font-stack fallbacks (Google Fonts that render in Playwright capture).
|
|
47
|
+
|
|
48
|
+
## Slide Layouts
|
|
49
|
+
Enumerate the slide archetypes this design system supports. ALWAYS include
|
|
50
|
+
at least these five:
|
|
51
|
+
- **Cover** — single dominant headline, no nav, no CTA buttons
|
|
52
|
+
- **Section divider** — short anchor headline, optional eyebrow
|
|
53
|
+
- **Content** — copy + single supporting visual (60/40 or 50/50 split)
|
|
54
|
+
- **Statistic** — one oversized number, one short caption
|
|
55
|
+
- **Closing** — final thesis line, footer/page-number strip
|
|
56
|
+
|
|
57
|
+
For each layout, describe the dominant rhythm in 1-3 bullets — what scale
|
|
58
|
+
carries the slide, what's allowed in the corners, what's NOT allowed.
|
|
59
|
+
|
|
60
|
+
## Signature Motifs
|
|
61
|
+
Two-to-five small visual elements that make the deck recognizably this brand.
|
|
62
|
+
Examples: a colored underline, a strike-through wordmark, a dot cluster,
|
|
63
|
+
a serif italic accent on one word per slide. Each motif MUST be reproducible
|
|
64
|
+
purely with inline HTML + CSS on a single slide.
|
|
65
|
+
|
|
66
|
+
## Avoid
|
|
67
|
+
Explicit list of web-only patterns the slides MUST NOT carry. ALWAYS include:
|
|
68
|
+
- top-nav bars, sticky headers, menu rows
|
|
69
|
+
- clickable CTA buttons ("Sign up", "Start free trial", "Read docs")
|
|
70
|
+
- multi-column footer-bands beyond a single thin strip with attribution + page number
|
|
71
|
+
- pricing tier grids
|
|
72
|
+
- hover/focus/active state styling
|
|
73
|
+
- atmospheric multi-stop gradients (unless the source spec mandates them)
|
|
74
|
+
Plus any AVOID rules carried over from the source DESIGN.md.
|
|
75
|
+
|
|
76
|
+
## Source mapping (for traceability)
|
|
77
|
+
A short bullet list showing how each web concept was mapped. Format:
|
|
78
|
+
- `<web concept from DESIGN.md>` → `<slide concept in DESIGN.slides.md>` OR `dropped`
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Web → slide mapping rules
|
|
82
|
+
|
|
83
|
+
Apply these mappings while converting. The left column is what DESIGN.md
|
|
84
|
+
contains; the right column is the slide-appropriate replacement.
|
|
85
|
+
|
|
86
|
+
| DESIGN.md (web) | DESIGN.slides.md (slide) |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `top-nav` / sticky header | A 12pt-tall mono **eyebrow strip** at the top of each slide with section number + brand wordmark — never a horizontal menu of items |
|
|
89
|
+
| `hero-band` (h1 + sub + dual CTA + illustration card) | **Cover layout** — single oversized headline, single sub, footer strip with page number. CTA buttons → discarded (no clicks possible). Illustration card → optional single visual anchor |
|
|
90
|
+
| `feature-card grid (3-up / 4-up)` | **Content layout** with 3-up grid OK, but cards must be cardless-leaning (whitespace-driven) — drop the marketing icon + blurb pattern, prefer pull quotes or per-track/per-member rows |
|
|
91
|
+
| `pricing tier grid` | **Discard entirely** — pricing has no slide analogue |
|
|
92
|
+
| `connector tile grid` | **Discard or convert to small badge row** — never a 4×4 grid of generic logos |
|
|
93
|
+
| `CTA banner` (full-bleed coral / brand) | **Section divider** or **closing** layout — keep the full-bleed color, replace CTA buttons with a thesis sentence and a page number |
|
|
94
|
+
| `product-mockup-card-dark` (code editor screenshot card) | **Content motif** — fine to keep as a single dark surface inside a content slide, but it can NOT dominate every slide |
|
|
95
|
+
| `footer-band` (4-column legal/sitemap) | **Single thin footer strip** — at most 1 line: brand wordmark left, page number right |
|
|
96
|
+
| `button-primary` / `button-secondary` | **Kicker text only** — write the verb as plain text ("Read the next issue") without box/border/click affordance |
|
|
97
|
+
| Hover / focus / pressed / disabled states | **Drop all of them** — slides are static |
|
|
98
|
+
| Multi-stop atmospheric gradients | **Drop unless source spec calls for one or two** — convert to a single accent shape |
|
|
99
|
+
| Spike-mark / asterisk-style brand glyph | **Keep as a small motif** — inline SVG at the slide eyebrow or footer |
|
|
100
|
+
|
|
101
|
+
## Preservation rules (do NOT translate away)
|
|
102
|
+
|
|
103
|
+
The conversion MUST carry over from DESIGN.md unchanged:
|
|
104
|
+
|
|
105
|
+
- The **color palette** (canvas, surface, ink, primary accent, secondary accents)
|
|
106
|
+
- The **type pairing** — display face vs body face vs mono face
|
|
107
|
+
- The **mood / atmosphere** — warm-editorial vs dark-product vs pastel-pop is the
|
|
108
|
+
whole reason the user imported this DESIGN.md; the slide deck must still feel
|
|
109
|
+
like the source brand
|
|
110
|
+
- **Brand-specific signature motifs** (spike-mark, hard offset shadow, slab-serif italic
|
|
111
|
+
accent, etc.) — translate the surface, not the identity
|
|
112
|
+
|
|
113
|
+
## Process checklist for the agent
|
|
114
|
+
|
|
115
|
+
1. Read `./DESIGN.md` in full.
|
|
116
|
+
2. Identify which sections are web-only and slot them through the mapping table.
|
|
117
|
+
3. Identify the design tokens (colors, type, spacing) that survive unchanged.
|
|
118
|
+
4. Draft `./DESIGN.slides.md` using the Output Contract template.
|
|
119
|
+
5. Show the user a short summary: 5–10 lines covering kept tokens + dropped
|
|
120
|
+
web sections + new slide layouts inferred.
|
|
121
|
+
6. Wait for explicit user approval ("looks good" or specific edits) before
|
|
122
|
+
moving to outline / slide generation.
|
|
123
|
+
7. Once approved, record `style: ./DESIGN.slides.md` in `slide-outline.md`.
|
|
124
|
+
|
|
125
|
+
## When DESIGN.slides.md should be re-converted
|
|
126
|
+
|
|
127
|
+
Suggest a re-conversion when:
|
|
128
|
+
- The source `DESIGN.md` is replaced / re-imported with a different brand
|
|
129
|
+
- The user explicitly asks for a different deck flavor (e.g. "more editorial",
|
|
130
|
+
"less code-heavy")
|
|
131
|
+
- A slide deck visibly carries web-only artifacts (nav bars, CTAs, footers
|
|
132
|
+
with link columns) that the current DESIGN.slides.md doesn't forbid
|
|
133
|
+
|
|
134
|
+
A `DESIGN.slides.md` is never regenerated automatically. It is the agent's
|
|
135
|
+
job, in conversation with the user.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# slides-grab Plan Reference
|
|
2
|
+
|
|
3
|
+
Use this bundled reference when planning a new deck.
|
|
4
|
+
|
|
5
|
+
## Stage boundary
|
|
6
|
+
- Planning stops at an approved `slide-outline.md`.
|
|
7
|
+
- Do not generate `slide-*.html` in this stage.
|
|
8
|
+
- Do not start design until the user explicitly approves the outline.
|
|
9
|
+
|
|
10
|
+
## Expected `slide-outline.md` shape
|
|
11
|
+
|
|
12
|
+
```markdown
|
|
13
|
+
# [Presentation Title]
|
|
14
|
+
|
|
15
|
+
## Meta
|
|
16
|
+
- **Topic**: ...
|
|
17
|
+
- **Target Audience**: ...
|
|
18
|
+
- **Tone/Mood**: ...
|
|
19
|
+
- **Slide Count**: N slides
|
|
20
|
+
- **Aspect Ratio**: 16:9
|
|
21
|
+
|
|
22
|
+
## Slide Composition
|
|
23
|
+
|
|
24
|
+
### Slide 1 - Cover
|
|
25
|
+
- **Type**: Cover
|
|
26
|
+
- **Title**: ...
|
|
27
|
+
- **Subtitle**: ...
|
|
28
|
+
|
|
29
|
+
### Slide 2 - Table of Contents
|
|
30
|
+
- **Type**: Contents
|
|
31
|
+
- **Items**: ...
|
|
32
|
+
|
|
33
|
+
### Slide 3 - [Title]
|
|
34
|
+
- **Type**: Section Divider / Content / Statistics / Quote / Timeline / ...
|
|
35
|
+
- **Key Message**: ...
|
|
36
|
+
- **Details**:
|
|
37
|
+
- ...
|
|
38
|
+
|
|
39
|
+
### Slide N - Closing
|
|
40
|
+
- **Type**: Closing
|
|
41
|
+
- **Message**: ...
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Planning reminders
|
|
45
|
+
- Keep the outline concise and reviewable.
|
|
46
|
+
- Present total slides, slide order, and each slide's role to the user.
|
|
47
|
+
- Revise until explicit approval.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Plan Skill - Presentation Outline Planning
|
|
2
|
+
|
|
3
|
+
A **supervisor skill** that takes a user topic, generates a `slide-outline.md` outline, and manages a revision loop until the user approves.
|
|
4
|
+
|
|
5
|
+
Keeps the outline process separate from slide design. Use a runtime-native subagent/task for drafting when available, or draft directly from this reference when the current runtime has no subagent mechanism.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Role Assignment
|
|
10
|
+
|
|
11
|
+
| Role | Owner | Responsibility |
|
|
12
|
+
|------|-------|----------------|
|
|
13
|
+
| **Supervisor** | plan-skill (you) | User communication, quality control, revision loop management |
|
|
14
|
+
| **Worker** | Outline worker | Draft and revise `slide-outline.md` |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Input
|
|
19
|
+
|
|
20
|
+
- User topic (required)
|
|
21
|
+
- Research results (optional)
|
|
22
|
+
- Reference materials, tone/mood requests, etc.
|
|
23
|
+
|
|
24
|
+
## Output
|
|
25
|
+
|
|
26
|
+
- User-approved `slide-outline.md`
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Workflow
|
|
31
|
+
|
|
32
|
+
### 1. Create the Outline Draft
|
|
33
|
+
|
|
34
|
+
Use a runtime-native task/subagent to generate a `slide-outline.md` draft when available. If the runtime has no such mechanism, write the draft directly using the format below.
|
|
35
|
+
|
|
36
|
+
**Include in the prompt:**
|
|
37
|
+
- User topic and requirements
|
|
38
|
+
- Research results (if available)
|
|
39
|
+
- Tone/mood requests
|
|
40
|
+
- Expected format for `slide-outline.md` (see format below)
|
|
41
|
+
|
|
42
|
+
### 2. Present Outline to User
|
|
43
|
+
|
|
44
|
+
Read the generated `slide-outline.md` and present to the user:
|
|
45
|
+
|
|
46
|
+
- Total number of slides
|
|
47
|
+
- Slide order and each slide's role
|
|
48
|
+
- Key message summary
|
|
49
|
+
- Design tone/mood
|
|
50
|
+
|
|
51
|
+
### 3. Feedback Revision Loop
|
|
52
|
+
|
|
53
|
+
When user provides feedback:
|
|
54
|
+
1. Organize the feedback
|
|
55
|
+
2. Revise the existing `slide-outline.md` with the feedback, using a runtime-native task/subagent when available
|
|
56
|
+
3. Present the revised outline to the user
|
|
57
|
+
4. Repeat until user approves
|
|
58
|
+
|
|
59
|
+
### 4. Approval Confirmation
|
|
60
|
+
|
|
61
|
+
Complete the outline stage when the user explicitly approves.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Absolute Rules
|
|
66
|
+
|
|
67
|
+
1. **Never proceed to the next stage without approval** — Maintain the revision loop until the user explicitly signals approval ("looks good", "approved", "OK", "proceed", etc.).
|
|
68
|
+
2. **Keep the outline stage separate** — Do not generate slide HTML while drafting or revising the outline.
|
|
69
|
+
3. **Never start HTML generation** — This skill's scope ends at `slide-outline.md` approval. HTML generation is the responsibility of `design-skill`.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Expected slide-outline.md Format
|
|
74
|
+
|
|
75
|
+
```markdown
|
|
76
|
+
# [Presentation Title]
|
|
77
|
+
|
|
78
|
+
## Meta
|
|
79
|
+
- **Topic**: ...
|
|
80
|
+
- **Target Audience**: ...
|
|
81
|
+
- **Tone/Mood**: ...
|
|
82
|
+
- **Slide Count**: N slides
|
|
83
|
+
- **Aspect Ratio**: 16:9
|
|
84
|
+
|
|
85
|
+
## Slide Composition
|
|
86
|
+
|
|
87
|
+
### Slide 1 - Cover
|
|
88
|
+
- **Type**: Cover
|
|
89
|
+
- **Title**: ...
|
|
90
|
+
- **Subtitle**: ...
|
|
91
|
+
|
|
92
|
+
### Slide 2 - Table of Contents
|
|
93
|
+
- **Type**: Contents
|
|
94
|
+
- **Items**: ...
|
|
95
|
+
|
|
96
|
+
### Slide 3 - [Title]
|
|
97
|
+
- **Type**: Section Divider / Content / Statistics / Quote / Timeline / ...
|
|
98
|
+
- **Key Message**: ...
|
|
99
|
+
- **Details**:
|
|
100
|
+
- ...
|
|
101
|
+
- ...
|
|
102
|
+
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
### Slide N - Closing
|
|
106
|
+
- **Type**: Closing
|
|
107
|
+
- **Message**: ...
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Outline Worker Prompt Examples
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
Runtime-native task/subagent prompt:
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
Create a presentation outline for the following topic.
|
|
119
|
+
|
|
120
|
+
Topic: [user topic]
|
|
121
|
+
Requirements: [user requirements]
|
|
122
|
+
Research results: [if available]
|
|
123
|
+
|
|
124
|
+
Save as slide-outline.md.
|
|
125
|
+
[include expected format]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
For feedback revisions:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Runtime-native task/subagent prompt:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
Revise the existing outline.
|
|
135
|
+
|
|
136
|
+
Current outline: [slide-outline.md content]
|
|
137
|
+
User feedback: [feedback content]
|
|
138
|
+
|
|
139
|
+
Save the revised slide-outline.md.
|
|
140
|
+
```
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { mkdir, readFile, readFile as readFileAsync, rm } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join, isAbsolute } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
createProvider as defaultCreateProvider,
|
|
9
|
+
resolveConfig as defaultResolveConfig,
|
|
10
|
+
} from 'god-tibo-imagen';
|
|
11
|
+
|
|
12
|
+
export const CODEX_DEFAULT_MODEL = 'gpt-5.4';
|
|
13
|
+
export const CODEX_PROVIDER_AUTO = 'auto';
|
|
14
|
+
export const CODEX_PROVIDER_PRIVATE_CODEX = 'private-codex';
|
|
15
|
+
export const CODEX_PROVIDER_CODEX_CLI = 'codex-cli';
|
|
16
|
+
|
|
17
|
+
const ASPECT_RATIO_HINTS = new Map([
|
|
18
|
+
['16:9', 'wide landscape 16:9 aspect ratio'],
|
|
19
|
+
['9:16', 'tall portrait 9:16 aspect ratio'],
|
|
20
|
+
['1:1', 'square 1:1 aspect ratio'],
|
|
21
|
+
['4:3', '4:3 aspect ratio'],
|
|
22
|
+
['3:4', 'portrait 3:4 aspect ratio'],
|
|
23
|
+
['3:2', 'landscape 3:2 aspect ratio'],
|
|
24
|
+
['2:3', 'portrait 2:3 aspect ratio'],
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
export function injectAspectRatioHint(prompt, aspectRatio) {
|
|
28
|
+
if (typeof prompt !== 'string' || prompt.trim() === '') {
|
|
29
|
+
throw new Error('injectAspectRatioHint: prompt must be a non-empty string.');
|
|
30
|
+
}
|
|
31
|
+
if (!aspectRatio || typeof aspectRatio !== 'string') {
|
|
32
|
+
return prompt;
|
|
33
|
+
}
|
|
34
|
+
const trimmed = aspectRatio.trim();
|
|
35
|
+
const explicit = ASPECT_RATIO_HINTS.get(trimmed);
|
|
36
|
+
if (explicit) {
|
|
37
|
+
return `${prompt} (${explicit})`;
|
|
38
|
+
}
|
|
39
|
+
if (/^\d+(?:\.\d+)?:\d+(?:\.\d+)?$/.test(trimmed)) {
|
|
40
|
+
return `${prompt} (${trimmed} aspect ratio)`;
|
|
41
|
+
}
|
|
42
|
+
return prompt;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function resolveCodexConfig({ providerMode = CODEX_PROVIDER_AUTO, resolveConfigImpl = defaultResolveConfig } = {}) {
|
|
46
|
+
return resolveConfigImpl({ provider: providerMode });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isCodexAuthError(error) {
|
|
50
|
+
if (!error || typeof error !== 'object') return false;
|
|
51
|
+
const code = String(error.code || '').toUpperCase();
|
|
52
|
+
if (code === 'UNAUTHORIZED' || code === 'ENOENT') return true;
|
|
53
|
+
const message = String(error.message || '').toLowerCase();
|
|
54
|
+
return (
|
|
55
|
+
message.includes('auth.json') ||
|
|
56
|
+
message.includes('unauthorized') ||
|
|
57
|
+
message.includes('chatgpt auth') ||
|
|
58
|
+
message.includes('codex login')
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getCodexFallbackMessage(reason) {
|
|
63
|
+
const summary = typeof reason === 'string' && reason.trim()
|
|
64
|
+
? reason.trim()
|
|
65
|
+
: 'codex-imagen image generation failed.';
|
|
66
|
+
return `${summary} codex-imagen is the default image provider and reuses your local Codex ChatGPT login (~/.codex/auth.json). Run \`codex login\` once to enable it. Optional fallbacks: set OPENAI_API_KEY (OpenAI gpt-image-2) or GOOGLE_API_KEY/GEMINI_API_KEY (Nano Banana). If image generation credentials are unavailable, use web search and download the chosen image into ./assets/<file>.`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const MIME_BY_EXTENSION = {
|
|
70
|
+
'.png': 'image/png',
|
|
71
|
+
'.jpg': 'image/jpeg',
|
|
72
|
+
'.jpeg': 'image/jpeg',
|
|
73
|
+
'.webp': 'image/webp',
|
|
74
|
+
'.gif': 'image/gif',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Read reference image file paths into data URLs suitable for the Codex
|
|
79
|
+
* /responses input_image blocks. Accepts absolute paths or paths relative
|
|
80
|
+
* to process.cwd(). Already-formed data: URLs and http(s) URLs are passed
|
|
81
|
+
* through as-is.
|
|
82
|
+
*/
|
|
83
|
+
export async function resolveReferenceImages(paths) {
|
|
84
|
+
const list = Array.isArray(paths) ? paths : [];
|
|
85
|
+
const dataUrls = [];
|
|
86
|
+
for (const entry of list) {
|
|
87
|
+
if (typeof entry !== 'string' || !entry.trim()) continue;
|
|
88
|
+
const value = entry.trim();
|
|
89
|
+
if (value.startsWith('data:') || value.startsWith('http://') || value.startsWith('https://')) {
|
|
90
|
+
dataUrls.push(value);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const resolved = isAbsolute(value) ? value : join(process.cwd(), value);
|
|
94
|
+
if (!existsSync(resolved)) {
|
|
95
|
+
throw new Error(`Reference image not found: ${value} (resolved ${resolved})`);
|
|
96
|
+
}
|
|
97
|
+
const ext = resolved.toLowerCase().match(/\.(png|jpe?g|webp|gif)$/);
|
|
98
|
+
const mime = ext ? MIME_BY_EXTENSION[`.${ext[1]}`] : 'image/png';
|
|
99
|
+
const bytes = await readFileAsync(resolved);
|
|
100
|
+
const base64 = bytes.toString('base64');
|
|
101
|
+
dataUrls.push(`data:${mime};base64,${base64}`);
|
|
102
|
+
}
|
|
103
|
+
return dataUrls;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function generateCodexImage({
|
|
107
|
+
prompt,
|
|
108
|
+
model = CODEX_DEFAULT_MODEL,
|
|
109
|
+
aspectRatio,
|
|
110
|
+
providerMode = CODEX_PROVIDER_AUTO,
|
|
111
|
+
dryRun = false,
|
|
112
|
+
referenceImages = [],
|
|
113
|
+
deps = {},
|
|
114
|
+
} = {}) {
|
|
115
|
+
if (typeof prompt !== 'string' || prompt.trim() === '') {
|
|
116
|
+
throw new Error('generateCodexImage: prompt must be a non-empty string.');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const createProviderImpl = deps.createProvider || defaultCreateProvider;
|
|
120
|
+
const resolveConfigImpl = deps.resolveConfig || defaultResolveConfig;
|
|
121
|
+
|
|
122
|
+
const enrichedPrompt = injectAspectRatioHint(prompt, aspectRatio);
|
|
123
|
+
const config = resolveCodexConfig({ providerMode, resolveConfigImpl });
|
|
124
|
+
const provider = createProviderImpl(config);
|
|
125
|
+
|
|
126
|
+
// Reference images are read into data URLs so the codex provider can
|
|
127
|
+
// pass them as input_image blocks in the Codex /responses request.
|
|
128
|
+
// buildResponsesRequest already accepts an images: string[] array of
|
|
129
|
+
// data URLs / URLs — we just need to surface them here.
|
|
130
|
+
const images = await resolveReferenceImages(referenceImages);
|
|
131
|
+
|
|
132
|
+
// codex's provider.generateImage writes a PNG to outputPath as a side
|
|
133
|
+
// effect. slides-grab centralizes asset path resolution in
|
|
134
|
+
// saveNanoBananaImage (src/nano-banana.js) - so we route codex's write
|
|
135
|
+
// to a tmp file and read the bytes back, letting the caller persist via
|
|
136
|
+
// the existing asset contract. This keeps the asset path policy single-sourced.
|
|
137
|
+
const tempDir = join(tmpdir(), `slides-grab-codex-${randomUUID()}`);
|
|
138
|
+
await mkdir(tempDir, { recursive: true });
|
|
139
|
+
const tempPath = join(tempDir, 'image.png');
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const result = await provider.generateImage({
|
|
143
|
+
prompt: enrichedPrompt,
|
|
144
|
+
model,
|
|
145
|
+
outputPath: tempPath,
|
|
146
|
+
dryRun: Boolean(dryRun),
|
|
147
|
+
images: images.length > 0 ? images : undefined,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (dryRun) {
|
|
151
|
+
return {
|
|
152
|
+
mimeType: 'image/png',
|
|
153
|
+
bytes: Buffer.alloc(0),
|
|
154
|
+
mode: result?.mode || 'dry-run',
|
|
155
|
+
warnings: Array.isArray(result?.warnings) ? result.warnings : [],
|
|
156
|
+
revisedPrompt: result?.revisedPrompt ?? null,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const bytes = await readFile(tempPath);
|
|
161
|
+
return {
|
|
162
|
+
mimeType: 'image/png',
|
|
163
|
+
bytes,
|
|
164
|
+
mode: result?.mode || 'live',
|
|
165
|
+
warnings: Array.isArray(result?.warnings) ? result.warnings : [],
|
|
166
|
+
revisedPrompt: result?.revisedPrompt ?? null,
|
|
167
|
+
};
|
|
168
|
+
} catch (error) {
|
|
169
|
+
const wrapped = new Error(getCodexFallbackMessage(error?.message || String(error)));
|
|
170
|
+
wrapped.cause = error;
|
|
171
|
+
wrapped.isAuthError = isCodexAuthError(error);
|
|
172
|
+
throw wrapped;
|
|
173
|
+
} finally {
|
|
174
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const __test_only__ = {
|
|
179
|
+
ASPECT_RATIO_HINTS,
|
|
180
|
+
isCodexAuthError,
|
|
181
|
+
resolveReferenceImages,
|
|
182
|
+
};
|