mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-12 13:59:47 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e7255efdd6 | |||
| b15a95e73d |
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Skills shared by Baoyu for improving daily work efficiency",
|
||||
"version": "1.18.3"
|
||||
"version": "1.20.0"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
@@ -2,6 +2,31 @@
|
||||
|
||||
English | [中文](./CHANGELOG.zh.md)
|
||||
|
||||
## 1.20.0 - 2026-01-24
|
||||
|
||||
### Features
|
||||
- `baoyu-cover-image`: upgrades from Type × Style two-dimension system to **4-dimension system**—adds `--text` dimension (none, title-only, title-subtitle, text-rich) for text density control and `--mood` dimension (subtle, balanced, bold) for emotional intensity. New `--quick` flag skips confirmation and uses auto-selection.
|
||||
|
||||
### Documentation
|
||||
- `baoyu-cover-image`: adds dimension reference files—`references/dimensions/text.md` (text density levels) and `references/dimensions/mood.md` (mood intensity levels).
|
||||
- `baoyu-cover-image`: updates base-prompt, first-time-setup, and preferences-schema to support new 4-dimension system with v2 schema.
|
||||
- `README.md`, `README.zh.md`: updates baoyu-cover-image documentation to reflect new 4-dimension system with `--text`, `--mood`, and `--quick` options.
|
||||
|
||||
## 1.19.0 - 2026-01-24
|
||||
|
||||
### Features
|
||||
- `baoyu-comic`: adds partial workflow options—`--storyboard-only`, `--prompts-only`, `--images-only`, and `--regenerate N` for flexible workflow control.
|
||||
- `baoyu-image-gen`: adds `--imageSize` parameter for Google providers (1K/2K/4K), changes default quality to 2k.
|
||||
- `baoyu-image-gen`: adds `GEMINI_API_KEY` as alias for `GOOGLE_API_KEY`.
|
||||
|
||||
### Refactor
|
||||
- `baoyu-comic`: extracts detailed workflow to `references/workflow.md`, reduces SKILL.md by ~400 lines while preserving functionality.
|
||||
- `baoyu-comic`: extracts content signal analysis to `references/auto-selection.md` and partial workflow docs to `references/partial-workflows.md`.
|
||||
- `baoyu-image-gen`: modularizes code—extracts types to `types.ts`, provider implementations to `providers/google.ts` and `providers/openai.ts`.
|
||||
|
||||
### Documentation
|
||||
- `baoyu-comic`: improves ohmsha preset documentation with explicit default Doraemon character definitions and visual descriptions.
|
||||
|
||||
## 1.18.3 - 2026-01-23
|
||||
|
||||
### Documentation
|
||||
|
||||
@@ -2,6 +2,31 @@
|
||||
|
||||
[English](./CHANGELOG.md) | 中文
|
||||
|
||||
## 1.20.0 - 2026-01-24
|
||||
|
||||
### 新功能
|
||||
- `baoyu-cover-image`:从类型 × 风格二维系统升级为**四维系统**——新增 `--text` 维度(none 无文字、title-only 仅标题、title-subtitle 标题+副标题、text-rich 丰富文字)控制文字密度,新增 `--mood` 维度(subtle 低调、balanced 平衡、bold 醒目)控制情感强度。新增 `--quick` 标志跳过确认,直接使用自动选择。
|
||||
|
||||
### 文档
|
||||
- `baoyu-cover-image`:新增维度参考文件——`references/dimensions/text.md`(文字密度级别)和 `references/dimensions/mood.md`(氛围强度级别)。
|
||||
- `baoyu-cover-image`:更新 base-prompt、first-time-setup 和 preferences-schema 以支持新的四维系统及 v2 配置模式。
|
||||
- `README.md`、`README.zh.md`:更新 baoyu-cover-image 文档,反映新的四维系统及 `--text`、`--mood`、`--quick` 选项。
|
||||
|
||||
## 1.19.0 - 2026-01-24
|
||||
|
||||
### 新功能
|
||||
- `baoyu-comic`:新增部分工作流选项——`--storyboard-only`、`--prompts-only`、`--images-only` 和 `--regenerate N`,实现灵活的工作流控制。
|
||||
- `baoyu-image-gen`:新增 `--imageSize` 参数用于 Google 提供商(1K/2K/4K),默认质量改为 2k。
|
||||
- `baoyu-image-gen`:新增 `GEMINI_API_KEY` 作为 `GOOGLE_API_KEY` 的别名。
|
||||
|
||||
### 重构
|
||||
- `baoyu-comic`:将详细工作流提取至 `references/workflow.md`,SKILL.md 减少约 400 行,功能完整保留。
|
||||
- `baoyu-comic`:将内容信号分析提取至 `references/auto-selection.md`,部分工作流文档提取至 `references/partial-workflows.md`。
|
||||
- `baoyu-image-gen`:代码模块化——类型定义提取至 `types.ts`,provider 实现提取至 `providers/google.ts` 和 `providers/openai.ts`。
|
||||
|
||||
### 文档
|
||||
- `baoyu-comic`:改进 ohmsha 预设文档,明确默认哆啦A梦角色定义和视觉描述。
|
||||
|
||||
## 1.18.3 - 2026-01-23
|
||||
|
||||
### 文档
|
||||
|
||||
@@ -245,24 +245,31 @@ Generate professional infographics with 20 layout types and 17 visual styles. An
|
||||
|
||||
#### baoyu-cover-image
|
||||
|
||||
Generate cover images for articles with Type × Style two-dimension system.
|
||||
Generate cover images for articles with 4 dimensions: Type × Style × Text × Mood.
|
||||
|
||||
```bash
|
||||
# Auto-select type and style based on content
|
||||
# Auto-select all dimensions based on content
|
||||
/baoyu-cover-image path/to/article.md
|
||||
|
||||
# Specify type and/or style
|
||||
# Quick mode: skip confirmation, use auto-selection
|
||||
/baoyu-cover-image path/to/article.md --quick
|
||||
|
||||
# Specify dimensions
|
||||
/baoyu-cover-image path/to/article.md --type conceptual --style blueprint
|
||||
/baoyu-cover-image path/to/article.md --style warm
|
||||
/baoyu-cover-image path/to/article.md --text title-subtitle --mood bold
|
||||
|
||||
# Specify aspect ratio (default: 2.35:1)
|
||||
/baoyu-cover-image path/to/article.md --aspect 16:9
|
||||
|
||||
# Without title text
|
||||
# Visual only (no title text)
|
||||
/baoyu-cover-image path/to/article.md --no-title
|
||||
```
|
||||
|
||||
Available types: `hero`, `conceptual`, `typography`, `metaphor`, `scene`, `minimal`
|
||||
**Four Dimensions**:
|
||||
- **Type**: `hero`, `conceptual`, `typography`, `metaphor`, `scene`, `minimal`
|
||||
- **Style**: 20 built-in styles (see previews below)
|
||||
- **Text**: `none`, `title-only` (default), `title-subtitle`, `text-rich`
|
||||
- **Mood**: `subtle`, `balanced` (default), `bold`
|
||||
|
||||
Available styles: `elegant` (default), `blueprint`, `bold-editorial`, `chalkboard`, `dark-atmospheric`, `editorial-infographic`, `fantasy-animation`, `flat-doodle`, `intuition-machine`, `minimal`, `nature`, `notion`, `pixel-art`, `playful`, `retro`, `sketch-notes`, `vector-illustration`, `vintage`, `warm`, `watercolor`
|
||||
|
||||
|
||||
+13
-6
@@ -245,24 +245,31 @@ npx skills add jimliu/baoyu-skills
|
||||
|
||||
#### baoyu-cover-image
|
||||
|
||||
为文章生成封面图,支持类型 × 风格二维系统。
|
||||
为文章生成封面图,支持四维定制系统:类型 × 风格 × 文字 × 氛围。
|
||||
|
||||
```bash
|
||||
# 根据内容自动选择类型和风格
|
||||
# 根据内容自动选择所有维度
|
||||
/baoyu-cover-image path/to/article.md
|
||||
|
||||
# 指定类型和/或风格
|
||||
# 快速模式:跳过确认,使用自动选择
|
||||
/baoyu-cover-image path/to/article.md --quick
|
||||
|
||||
# 指定维度
|
||||
/baoyu-cover-image path/to/article.md --type conceptual --style blueprint
|
||||
/baoyu-cover-image path/to/article.md --style warm
|
||||
/baoyu-cover-image path/to/article.md --text title-subtitle --mood bold
|
||||
|
||||
# 指定宽高比(默认:2.35:1)
|
||||
/baoyu-cover-image path/to/article.md --aspect 16:9
|
||||
|
||||
# 不包含标题文字
|
||||
# 纯视觉(不含标题文字)
|
||||
/baoyu-cover-image path/to/article.md --no-title
|
||||
```
|
||||
|
||||
可用类型:`hero`、`conceptual`、`typography`、`metaphor`、`scene`、`minimal`
|
||||
**四个维度**:
|
||||
- **类型 (Type)**:`hero`、`conceptual`、`typography`、`metaphor`、`scene`、`minimal`
|
||||
- **风格 (Style)**:20 种内置风格(见下方预览)
|
||||
- **文字 (Text)**:`none`、`title-only`(默认)、`title-subtitle`、`text-rich`
|
||||
- **氛围 (Mood)**:`subtle`、`balanced`(默认)、`bold`
|
||||
|
||||
可用风格:`elegant`(默认)、`blueprint`、`bold-editorial`、`chalkboard`、`dark-atmospheric`、`editorial-infographic`、`fantasy-animation`、`flat-doodle`、`intuition-machine`、`minimal`、`nature`、`notion`、`pixel-art`、`playful`、`retro`、`sketch-notes`、`vector-illustration`、`vintage`、`warm`、`watercolor`
|
||||
|
||||
|
||||
+128
-381
@@ -27,6 +27,17 @@ Create original knowledge comics with flexible art style × tone combinations.
|
||||
| `--aspect` | 3:4 (default, portrait), 4:3 (landscape), 16:9 (widescreen) | Page aspect ratio |
|
||||
| `--lang` | auto (default), zh, en, ja, etc. | Output language |
|
||||
|
||||
### Partial Workflow Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--storyboard-only` | Generate storyboard only, skip prompts and images |
|
||||
| `--prompts-only` | Generate storyboard + prompts, skip images |
|
||||
| `--images-only` | Generate images from existing prompts directory |
|
||||
| `--regenerate N` | Regenerate specific page(s) only (e.g., `3` or `2,5,8`) |
|
||||
|
||||
Details: [references/partial-workflows.md](references/partial-workflows.md)
|
||||
|
||||
### Art Styles (画风)
|
||||
|
||||
| Style | 中文 | Description |
|
||||
@@ -69,27 +80,25 @@ Presets with special rules beyond art+tone:
|
||||
| ink-brush | neutral, dramatic, action, vintage | warm | romantic, energetic |
|
||||
| chalk | neutral, warm, energetic | vintage | dramatic, action, romantic |
|
||||
|
||||
Art Style × Tone × Layout can be freely combined. Incompatible combinations work but may produce unexpected results.
|
||||
Details: [references/auto-selection.md](references/auto-selection.md)
|
||||
|
||||
## Auto Selection
|
||||
|
||||
Content signals determine default art + tone + layout (or preset):
|
||||
|
||||
| Content Signals | Art Style | Tone | Layout | Preset |
|
||||
|-----------------|-----------|------|--------|--------|
|
||||
| Tutorial, how-to, beginner | manga | neutral | webtoon | **ohmsha** |
|
||||
| Computing, AI, programming | manga | neutral | dense | **ohmsha** |
|
||||
| Technical explanation, educational | manga | neutral | webtoon | **ohmsha** |
|
||||
| Pre-1950, classical, ancient | realistic | vintage | cinematic | - |
|
||||
| Personal story, mentor | ligne-claire | warm | standard | - |
|
||||
| Conflict, breakthrough | (inherit) | dramatic | splash | - |
|
||||
| Wine, food, business, lifestyle | realistic | neutral | cinematic | - |
|
||||
| Martial arts, wuxia, xianxia | ink-brush | action | splash | **wuxia** |
|
||||
| Romance, love, school life | manga | romantic | standard | **shoujo** |
|
||||
| Biography, balanced | ligne-claire | neutral | mixed | - |
|
||||
| Content Signals | Recommended |
|
||||
|-----------------|-------------|
|
||||
| Tutorial, how-to, programming, educational | **ohmsha** preset |
|
||||
| Pre-1950, classical, ancient | realistic + vintage |
|
||||
| Personal story, mentor | ligne-claire + warm |
|
||||
| Martial arts, wuxia | **wuxia** preset |
|
||||
| Romance, school life | **shoujo** preset |
|
||||
| Biography, balanced | ligne-claire + neutral |
|
||||
|
||||
**When preset is recommended**: Load `references/presets/{preset}.md` and apply all special rules.
|
||||
|
||||
Details: [references/auto-selection.md](references/auto-selection.md)
|
||||
|
||||
## Script Directory
|
||||
|
||||
**Important**: All scripts are located in the `scripts/` subdirectory of this skill.
|
||||
@@ -111,409 +120,147 @@ Output directory: `comic/{topic-slug}/`
|
||||
- Conflict: append timestamp (e.g., `turing-story-20260118-143052`)
|
||||
|
||||
**Contents**:
|
||||
- `source-{slug}.{ext}` - Source files
|
||||
- `analysis.md` - Content analysis
|
||||
- `storyboard.md` - Storyboard with panel breakdown
|
||||
- `characters/characters.md` - Character definitions
|
||||
- `characters/characters.png` - Character reference sheet
|
||||
- `prompts/NN-{cover|page}-[slug].md` - Generation prompts
|
||||
- `NN-{cover|page}-[slug].png` - Generated images
|
||||
- `{topic-slug}.pdf` - Final merged PDF
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `source-{slug}.{ext}` | Source files |
|
||||
| `analysis.md` | Content analysis |
|
||||
| `storyboard.md` | Storyboard with panel breakdown |
|
||||
| `characters/characters.md` | Character definitions |
|
||||
| `characters/characters.png` | Character reference sheet |
|
||||
| `prompts/NN-{cover\|page}-[slug].md` | Generation prompts |
|
||||
| `NN-{cover\|page}-[slug].png` | Generated images |
|
||||
| `{topic-slug}.pdf` | Final merged PDF |
|
||||
|
||||
## Language Handling
|
||||
|
||||
**Detection Priority**:
|
||||
1. `--lang` flag (explicit)
|
||||
2. EXTEND.md `language` setting
|
||||
3. User's conversation language
|
||||
4. Source content language
|
||||
|
||||
**Rule**: Use user's input language or saved language preference for ALL interactions:
|
||||
- Storyboard outlines and scene descriptions
|
||||
- Image generation prompts
|
||||
- User selection options and confirmations
|
||||
- Progress updates, questions, errors, summaries
|
||||
|
||||
Technical terms remain in English.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Progress Checklist
|
||||
|
||||
Copy and track progress:
|
||||
|
||||
```
|
||||
Comic Progress:
|
||||
- [ ] Step 1: Load preferences + Analyze content
|
||||
- [ ] Step 2: Confirmation 1 - Style & options ⚠️ REQUIRED
|
||||
- [ ] Step 1: Setup & Analyze (1.1 Preferences, 1.2 Analyze, 1.3 Check existing)
|
||||
- [ ] Step 2: Confirmation - Style & options ⚠️ REQUIRED
|
||||
- [ ] Step 3: Generate storyboard + characters
|
||||
- [ ] Step 4: Confirmation 2 - Review outline (if requested)
|
||||
- [ ] Step 5: Generate images (sequential)
|
||||
- [ ] Step 6: Merge to PDF
|
||||
- [ ] Step 7: Completion report
|
||||
- [ ] Step 4: Review outline (conditional)
|
||||
- [ ] Step 5: Generate prompts
|
||||
- [ ] Step 6: Review prompts (conditional)
|
||||
- [ ] Step 7: Generate images ⚠️ CHARACTER REF REQUIRED
|
||||
- [ ] 7.1 Generate character sheet FIRST → characters/characters.png
|
||||
- [ ] 7.2 Generate pages WITH --ref characters/characters.png
|
||||
- [ ] Step 8: Merge to PDF
|
||||
- [ ] Step 9: Completion report
|
||||
```
|
||||
|
||||
### Flow
|
||||
|
||||
```
|
||||
Input → Preferences → Analyze → [Confirm 1: Style + Skip?] → Storyboard → [Confirm 2: if needed] → Generate → PDF → Complete
|
||||
Input → Preferences → Analyze → [Check Existing?] → [Confirm: Style + Reviews] → Storyboard → [Review?] → Prompts → [Review?] → Images → PDF → Complete
|
||||
```
|
||||
|
||||
### Step 1: Setup & Analyze
|
||||
### Step Summary
|
||||
|
||||
**1.1 Load Preferences (EXTEND.md)**
|
||||
| Step | Action | Key Output |
|
||||
|------|--------|------------|
|
||||
| 1.1 | Load EXTEND.md preferences | Config loaded |
|
||||
| 1.2 | Analyze content | `analysis.md` |
|
||||
| 1.3 | Check existing directory | Handle conflicts |
|
||||
| 2 | Confirm style, focus, audience, reviews | User preferences |
|
||||
| 3 | Generate storyboard + characters | `storyboard.md`, `characters/` |
|
||||
| 4 | Review outline (if requested) | User approval |
|
||||
| 5 | Generate prompts | `prompts/*.md` |
|
||||
| 6 | Review prompts (if requested) | User approval |
|
||||
| **7.1** | **Generate character sheet FIRST** | `characters/characters.png` |
|
||||
| **7.2** | Generate pages **with character ref** | `*.png` files |
|
||||
| 8 | Merge to PDF | `{slug}.pdf` |
|
||||
| 9 | Completion report | Summary |
|
||||
|
||||
Use Bash to check EXTEND.md existence (priority order):
|
||||
### Step 7: Image Generation ⚠️ CRITICAL
|
||||
|
||||
**Character reference is MANDATORY for visual consistency.**
|
||||
|
||||
**7.1 Generate character sheet first**:
|
||||
```bash
|
||||
# Use Reference Sheet Prompt from characters/characters.md
|
||||
npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
|
||||
--promptfiles characters/characters.md \
|
||||
--image characters/characters.png --ar 4:3
|
||||
```
|
||||
|
||||
**7.2 Generate each page WITH character reference**:
|
||||
|
||||
| Skill Capability | Strategy |
|
||||
|------------------|----------|
|
||||
| Supports `--ref` | Pass `characters/characters.png` with EVERY page |
|
||||
| No `--ref` support | Prepend character descriptions to EVERY prompt file |
|
||||
|
||||
```bash
|
||||
# Check project-level first
|
||||
test -f .baoyu-skills/baoyu-comic/EXTEND.md && echo "project"
|
||||
|
||||
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
|
||||
test -f "$HOME/.baoyu-skills/baoyu-comic/EXTEND.md" && echo "user"
|
||||
```
|
||||
|
||||
┌──────────────────────────────────────────────┬───────────────────┐
|
||||
│ Path │ Location │
|
||||
├──────────────────────────────────────────────┼───────────────────┤
|
||||
│ .baoyu-skills/baoyu-comic/EXTEND.md │ Project directory │
|
||||
├──────────────────────────────────────────────┼───────────────────┤
|
||||
│ $HOME/.baoyu-skills/baoyu-comic/EXTEND.md │ User home │
|
||||
└──────────────────────────────────────────────┴───────────────────┘
|
||||
|
||||
**When EXTEND.md Found** → Read, parse, **output summary to user**:
|
||||
|
||||
```
|
||||
📋 Loaded preferences from [full path]
|
||||
├─ Watermark: [enabled/disabled] [content if enabled]
|
||||
├─ Art Style: [style name or "auto-select"]
|
||||
├─ Tone: [tone name or "auto-select"]
|
||||
├─ Layout: [layout or "auto-select"]
|
||||
├─ Language: [language or "auto-detect"]
|
||||
└─ Character presets: [count] defined
|
||||
```
|
||||
|
||||
**MUST output this summary** so user knows their current configuration. Do not skip or silently load.
|
||||
|
||||
**When EXTEND.md Not Found** → First-time setup:
|
||||
|
||||
1. Inform user: "No preferences found. Let's set up your defaults."
|
||||
2. Use AskUserQuestion to collect preferences (see `references/config/first-time-setup.md`)
|
||||
3. Create EXTEND.md at user-chosen location
|
||||
4. Confirm: "✓ Preferences saved to [path]"
|
||||
|
||||
**EXTEND.md Supports**: Watermark | Preferred art/tone/layout | Custom style definitions | Character presets | Language preference
|
||||
|
||||
Schema: `references/config/preferences-schema.md`
|
||||
|
||||
**Important**: Once EXTEND.md exists, watermark, language, and style defaults are NOT asked again in Confirmation 1 or 2. These are session-persistent settings.
|
||||
|
||||
**1.2 Analyze Content → `analysis.md`**
|
||||
|
||||
Read source content, save it if needed, and perform deep analysis.
|
||||
|
||||
**Actions**:
|
||||
1. **Save source content** (if not already a file):
|
||||
- If user provides a file path: use as-is
|
||||
- If user pastes content: save to `source.md` in target directory
|
||||
2. Read source content
|
||||
3. **Deep analysis** following `references/analysis-framework.md`:
|
||||
- Target audience identification
|
||||
- Value proposition for readers
|
||||
- Core themes and narrative potential
|
||||
- Key figures and their story arcs
|
||||
4. Detect source language
|
||||
5. **Determine language**:
|
||||
- If EXTEND.md has `language` → use it
|
||||
- Else if `--lang` option provided → use it
|
||||
- Else → use detected source language
|
||||
6. Determine recommended page count:
|
||||
- Short story: 5-8 pages
|
||||
- Medium complexity: 9-15 pages
|
||||
- Full biography: 16-25 pages
|
||||
7. Analyze content signals for art/tone/layout recommendations
|
||||
8. **Save to `analysis.md`**
|
||||
|
||||
**analysis.md Format**: YAML front matter (title, topic, time_span, source_language, user_language, aspect_ratio, recommended_page_count, recommended_art, recommended_tone) + sections for Target Audience, Value Proposition, Core Themes, Key Figures & Story Arcs, Content Signals, Recommended Approaches. See `references/analysis-framework.md` for full template.
|
||||
|
||||
### Step 2: Confirmation 1 - Style & Options ⚠️
|
||||
|
||||
**Purpose**: Select visual style + decide whether to review outline before generation. **Do NOT skip.**
|
||||
|
||||
**Note**: Watermark and language already configured in EXTEND.md (Step 1).
|
||||
|
||||
**Display summary**:
|
||||
- Content type + topic identified
|
||||
- Key figures extracted
|
||||
- Time span detected
|
||||
- Recommended page count
|
||||
- Language: [from EXTEND.md or detected]
|
||||
- **Recommended style**: [art] + [tone] (based on content signals)
|
||||
|
||||
**Use AskUserQuestion** for:
|
||||
|
||||
**Question 1: Visual Style**
|
||||
|
||||
If a preset is recommended (see Auto Selection), show it first:
|
||||
|
||||
```
|
||||
header: "Style"
|
||||
question: "Which visual style for this comic?"
|
||||
options:
|
||||
- label: "[preset name] preset (Recommended)" # If preset recommended
|
||||
description: "[preset description] - includes special rules"
|
||||
- label: "[recommended art] + [recommended tone] (Recommended)" # If no preset
|
||||
description: "Best match for your content based on analysis"
|
||||
- label: "ligne-claire + neutral"
|
||||
description: "Classic educational, Logicomix style"
|
||||
- label: "ohmsha preset"
|
||||
description: "Educational manga with visual metaphors, gadgets, NO talking heads"
|
||||
- label: "Custom"
|
||||
description: "Specify your own art + tone or preset"
|
||||
```
|
||||
|
||||
**Preset vs Art+Tone**: Presets include special rules beyond art+tone. `ohmsha` = manga + neutral + visual metaphor rules + character roles + NO talking heads. Plain `manga + neutral` does NOT include these rules.
|
||||
|
||||
**Question 2: Narrative Focus** (multiSelect: true)
|
||||
```
|
||||
header: "Focus"
|
||||
question: "What should the comic emphasize? (Select all that apply)"
|
||||
options:
|
||||
- label: "Biography/life story"
|
||||
description: "Follow a person's journey through key life events"
|
||||
- label: "Concept explanation"
|
||||
description: "Break down complex ideas visually"
|
||||
- label: "Historical event"
|
||||
description: "Dramatize important historical moments"
|
||||
- label: "Tutorial/how-to"
|
||||
description: "Step-by-step educational guide"
|
||||
```
|
||||
|
||||
**Question 3: Target Audience**
|
||||
```
|
||||
header: "Audience"
|
||||
question: "Who is the primary reader?"
|
||||
options:
|
||||
- label: "General readers"
|
||||
description: "Broad appeal, accessible content"
|
||||
- label: "Students/learners"
|
||||
description: "Educational focus, clear explanations"
|
||||
- label: "Industry professionals"
|
||||
description: "Technical depth, domain knowledge"
|
||||
- label: "Children/young readers"
|
||||
description: "Simplified language, engaging visuals"
|
||||
```
|
||||
|
||||
**Question 4: Outline Review**
|
||||
```
|
||||
header: "Review"
|
||||
question: "Do you want to review the outline before image generation?"
|
||||
options:
|
||||
- label: "Yes, let me review (Recommended)"
|
||||
description: "Review storyboard and characters before generating images"
|
||||
- label: "No, generate directly"
|
||||
description: "Skip outline review, start generating immediately"
|
||||
```
|
||||
|
||||
**After response**:
|
||||
1. Update `analysis.md` with user preferences
|
||||
2. **Store `skip_outline_review`** flag based on Question 4 response
|
||||
3. → Step 3
|
||||
|
||||
### Step 3: Generate Storyboard + Characters
|
||||
|
||||
Create storyboard and character definitions using the confirmed style from Step 2.
|
||||
|
||||
**Loading Style References**:
|
||||
- Art style: `references/art-styles/{art}.md`
|
||||
- Tone: `references/tones/{tone}.md`
|
||||
- If preset (ohmsha/wuxia/shoujo): also load `references/presets/{preset}.md`
|
||||
|
||||
**Generate**:
|
||||
|
||||
1. **Storyboard** (`storyboard.md`):
|
||||
- YAML front matter with art_style, tone, layout, aspect_ratio
|
||||
- Cover design
|
||||
- Each page: layout, panel breakdown, visual prompts
|
||||
- **Written in user's preferred language** (from Step 1)
|
||||
- Reference: `references/storyboard-template.md`
|
||||
- **If using preset**: Load and apply preset rules from `references/presets/`
|
||||
|
||||
2. **Character definitions** (`characters/characters.md`):
|
||||
- Visual specs matching the art style (in user's preferred language)
|
||||
- Include Reference Sheet Prompt for later image generation
|
||||
- Reference: `references/character-template.md`
|
||||
|
||||
**After generation**:
|
||||
- If `skip_outline_review` is true → Skip Step 4, go directly to Step 5
|
||||
- If `skip_outline_review` is false → Continue to Step 4
|
||||
|
||||
### Step 4: Confirmation 2 - Review Outline (Conditional)
|
||||
|
||||
**Skip this step** if user selected "No, generate directly" in Step 2.
|
||||
|
||||
**Purpose**: User reviews and confirms storyboard + characters before generation.
|
||||
|
||||
**Display**:
|
||||
- Page count and structure
|
||||
- Art style + Tone combination
|
||||
- Page-by-page summary (Cover → P1 → P2...)
|
||||
- Character list with brief descriptions
|
||||
|
||||
**Use AskUserQuestion**:
|
||||
|
||||
```
|
||||
header: "Confirm"
|
||||
question: "Ready to generate images with this outline?"
|
||||
options:
|
||||
- label: "Yes, proceed (Recommended)"
|
||||
description: "Generate character sheet and comic pages"
|
||||
- label: "Edit storyboard first"
|
||||
description: "I'll modify storyboard.md before continuing"
|
||||
- label: "Edit characters first"
|
||||
description: "I'll modify characters/characters.md before continuing"
|
||||
- label: "Edit both"
|
||||
description: "I'll modify both files before continuing"
|
||||
```
|
||||
|
||||
**After response**:
|
||||
1. If user wants to edit → Wait for user to finish editing, then ask again
|
||||
2. If user confirms → Continue to Step 5
|
||||
|
||||
### Step 5: Generate Images
|
||||
|
||||
With confirmed storyboard + art style + tone + aspect ratio:
|
||||
|
||||
**Style Reference Loading**:
|
||||
- Read `references/art-styles/{art}.md` for rendering guidelines
|
||||
- Read `references/tones/{tone}.md` for mood/color adjustments
|
||||
- If preset: Read `references/presets/{preset}.md` for special rules
|
||||
|
||||
**5.1 Generate Character Reference Sheet** (first):
|
||||
1. Use Reference Sheet Prompt from `characters/characters.md`
|
||||
2. Generate → `characters/characters.png`
|
||||
3. This ensures visual consistency for all subsequent pages
|
||||
|
||||
**5.2 Generate Comic Pages**:
|
||||
|
||||
**CRITICAL: Character Reference is MANDATORY** for visual consistency across all pages.
|
||||
|
||||
**Before generating any page**:
|
||||
1. Read the image generation skill's SKILL.md
|
||||
2. Check if it supports reference image input (`--ref`, `--reference`, etc.)
|
||||
3. Choose the appropriate strategy below
|
||||
|
||||
**Character Reference Strategy**:
|
||||
|
||||
| Skill Capability | Strategy | Action |
|
||||
|------------------|----------|--------|
|
||||
| Supports `--ref` | **Strategy A** | Pass `characters/characters.png` with EVERY page |
|
||||
| Does NOT support `--ref` | **Strategy B** | Prepend character descriptions to EVERY prompt |
|
||||
|
||||
**Strategy A: Using `--ref` parameter** (e.g., baoyu-image-gen)
|
||||
|
||||
```bash
|
||||
# Each page generation MUST include --ref
|
||||
# Example: ALWAYS include --ref for consistency
|
||||
npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
|
||||
--promptfiles prompts/01-page-xxx.md \
|
||||
--image 01-page-xxx.png \
|
||||
--ar 3:4 \
|
||||
--image 01-page-xxx.png --ar 3:4 \
|
||||
--ref characters/characters.png
|
||||
```
|
||||
|
||||
**Strategy B: Embedding character descriptions in prompt**
|
||||
**Full workflow details**: [references/workflow.md](references/workflow.md)
|
||||
|
||||
When skill does NOT support reference images, create combined prompt files:
|
||||
### EXTEND.md Paths
|
||||
|
||||
```markdown
|
||||
# prompts/01-page-xxx.md (with embedded character reference)
|
||||
| Path | Location |
|
||||
|------|----------|
|
||||
| `.baoyu-skills/baoyu-comic/EXTEND.md` | Project directory |
|
||||
| `$HOME/.baoyu-skills/baoyu-comic/EXTEND.md` | User home |
|
||||
|
||||
## Character Reference (maintain consistency)
|
||||
[Copy relevant sections from characters/characters.md here]
|
||||
- 大雄: Japanese boy, round glasses, yellow shirt, navy shorts...
|
||||
- 哆啦A梦: Round blue robot cat, white belly, red nose, golden bell...
|
||||
**EXTEND.md Supports**: Watermark | Preferred art/tone/layout | Custom style definitions | Character presets | Language preference
|
||||
|
||||
## Page Content
|
||||
[Original page prompt here]
|
||||
```
|
||||
|
||||
**For each page (cover + pages)**:
|
||||
1. Save prompt to `prompts/NN-{cover|page}-[slug].md`
|
||||
2. Generate image using Strategy A or B (based on skill capability)
|
||||
3. Report progress after each generation
|
||||
|
||||
**Watermark Application** (if enabled in preferences):
|
||||
Add to each image generation prompt:
|
||||
```
|
||||
Include a subtle watermark "[content]" positioned at [position]
|
||||
with approximately [opacity*100]% visibility. The watermark should
|
||||
be legible but not distracting from the comic panels and storytelling.
|
||||
Ensure watermark does not overlap speech bubbles or key action.
|
||||
```
|
||||
Reference: `references/config/watermark-guide.md`
|
||||
|
||||
**Session Management**:
|
||||
If image generation skill supports `--sessionId`:
|
||||
1. Generate unique session ID: `comic-{topic-slug}-{timestamp}`
|
||||
2. Use same session ID for all pages
|
||||
3. Ensures visual consistency across generated images
|
||||
|
||||
### Step 6: Merge to PDF
|
||||
|
||||
After all images generated:
|
||||
|
||||
```bash
|
||||
npx -y bun ${SKILL_DIR}/scripts/merge-to-pdf.ts <comic-dir>
|
||||
```
|
||||
|
||||
Creates `{topic-slug}.pdf` with all pages as full-page images.
|
||||
|
||||
### Step 7: Completion Report
|
||||
|
||||
```
|
||||
Comic Complete!
|
||||
Title: [title] | Art: [art] | Tone: [tone] | Pages: [count] | Aspect: [ratio] | Language: [lang]
|
||||
Watermark: [enabled/disabled]
|
||||
Location: [path]
|
||||
✓ analysis.md
|
||||
✓ characters.png
|
||||
✓ 00-cover-[slug].png ... NN-page-[slug].png
|
||||
✓ {topic-slug}.pdf
|
||||
```
|
||||
|
||||
## Page Modification
|
||||
|
||||
| Action | Steps |
|
||||
|--------|-------|
|
||||
| **Edit** | Update prompt → Regenerate image → Regenerate PDF |
|
||||
| **Add** | Create prompt at position → Generate image → Renumber subsequent (NN+1) → Update storyboard → Regenerate PDF |
|
||||
| **Delete** | Remove files → Renumber subsequent (NN-1) → Update storyboard → Regenerate PDF |
|
||||
|
||||
**File naming**: `NN-{cover|page}-[slug].png` (e.g., `03-page-enigma-machine.png`)
|
||||
- Slugs: kebab-case, unique, derived from content
|
||||
- Renumbering: Update NN prefix only, slugs unchanged
|
||||
|
||||
## Preset: Ohmsha
|
||||
|
||||
Default: Use Doraemon characters (大雄=learner, 哆啦A梦=mentor, 胖虎=challenge, 静香=support). Custom characters via `--characters "Student:小明,Mentor:教授"` or character presets in EXTEND.md.
|
||||
|
||||
Requirements: Visual metaphors, NO talking heads, narrative page titles.
|
||||
|
||||
See `references/presets/ohmsha.md` and `references/ohmsha-guide.md` for details.
|
||||
Schema: [references/config/preferences-schema.md](references/config/preferences-schema.md)
|
||||
|
||||
## References
|
||||
|
||||
Detailed templates and guidelines in `references/` directory:
|
||||
|
||||
**Core Templates**:
|
||||
- `analysis-framework.md` - Deep content analysis for comic adaptation
|
||||
- `character-template.md` - Character definition format and examples
|
||||
- `storyboard-template.md` - Storyboard structure and panel breakdown
|
||||
- `ohmsha-guide.md` - Ohmsha manga style specifics
|
||||
- [analysis-framework.md](references/analysis-framework.md) - Deep content analysis
|
||||
- [character-template.md](references/character-template.md) - Character definition format
|
||||
- [storyboard-template.md](references/storyboard-template.md) - Storyboard structure
|
||||
- [ohmsha-guide.md](references/ohmsha-guide.md) - Ohmsha manga specifics
|
||||
|
||||
**Style Definitions** (new 3-dimension system):
|
||||
- `art-styles/` - Art style definitions (ligne-claire, manga, realistic, ink-brush, chalk)
|
||||
- `tones/` - Tone definitions (neutral, warm, dramatic, romantic, energetic, vintage, action)
|
||||
- `presets/` - Preset shortcuts with special rules (ohmsha, wuxia, shoujo)
|
||||
- `layouts/` - Layout definitions (standard, cinematic, dense, splash, mixed, webtoon)
|
||||
**Style Definitions**:
|
||||
- `references/art-styles/` - Art styles (ligne-claire, manga, realistic, ink-brush, chalk)
|
||||
- `references/tones/` - Tones (neutral, warm, dramatic, romantic, energetic, vintage, action)
|
||||
- `references/presets/` - Presets with special rules (ohmsha, wuxia, shoujo)
|
||||
- `references/layouts/` - Layouts (standard, cinematic, dense, splash, mixed, webtoon)
|
||||
|
||||
**Workflow**:
|
||||
- [workflow.md](references/workflow.md) - Full workflow details
|
||||
- [auto-selection.md](references/auto-selection.md) - Content signal analysis
|
||||
- [partial-workflows.md](references/partial-workflows.md) - Partial workflow options
|
||||
|
||||
**Config**:
|
||||
- `config/preferences-schema.md` - EXTEND.md schema
|
||||
- `config/first-time-setup.md` - First-time setup flow
|
||||
- `config/watermark-guide.md` - Watermark configuration
|
||||
- [config/preferences-schema.md](references/config/preferences-schema.md) - EXTEND.md schema
|
||||
- [config/first-time-setup.md](references/config/first-time-setup.md) - First-time setup
|
||||
- [config/watermark-guide.md](references/config/watermark-guide.md) - Watermark configuration
|
||||
|
||||
## Notes
|
||||
|
||||
- Auto-retry once on failure | Cartoon alternatives for sensitive figures
|
||||
- Use confirmed language preference | Maintain style consistency
|
||||
- **Step 2 confirmation required** - do not skip (style selection + outline review option)
|
||||
- **Step 4 conditional** - only if user requested outline review in Step 2
|
||||
- Watermark/language configured once in EXTEND.md, not asked in confirmations
|
||||
|
||||
## Extension Support
|
||||
|
||||
Custom configurations via EXTEND.md. See **Step 1.1** for paths and supported options.
|
||||
- Image generation: 10-30 seconds per page
|
||||
- Auto-retry once on generation failure
|
||||
- Use stylized alternatives for sensitive public figures
|
||||
- Maintain style consistency via session ID
|
||||
- **Step 2 confirmation required** - do not skip
|
||||
- **Steps 4/6 conditional** - only if user requested in Step 2
|
||||
- **Step 7.1 character sheet MUST be generated before pages** - ensures consistency
|
||||
- **Step 7.2 EVERY page MUST reference characters** - use `--ref` or embed descriptions
|
||||
- Watermark/language configured once in EXTEND.md
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# Auto Selection
|
||||
|
||||
Content signals determine default art + tone + layout (or preset).
|
||||
|
||||
## Content Signal Matrix
|
||||
|
||||
| Content Signals | Art Style | Tone | Layout | Preset |
|
||||
|-----------------|-----------|------|--------|--------|
|
||||
| Tutorial, how-to, beginner | manga | neutral | webtoon | **ohmsha** |
|
||||
| Computing, AI, programming | manga | neutral | dense | **ohmsha** |
|
||||
| Technical explanation, educational | manga | neutral | webtoon | **ohmsha** |
|
||||
| Pre-1950, classical, ancient | realistic | vintage | cinematic | - |
|
||||
| Personal story, mentor | ligne-claire | warm | standard | - |
|
||||
| Conflict, breakthrough | (inherit) | dramatic | splash | - |
|
||||
| Wine, food, business, lifestyle | realistic | neutral | cinematic | - |
|
||||
| Martial arts, wuxia, xianxia | ink-brush | action | splash | **wuxia** |
|
||||
| Romance, love, school life | manga | romantic | standard | **shoujo** |
|
||||
| Biography, balanced | ligne-claire | neutral | mixed | - |
|
||||
|
||||
## Preset Recommendation Rules
|
||||
|
||||
**When preset is recommended**: Load `presets/{preset}.md` and apply all special rules.
|
||||
|
||||
### ohmsha
|
||||
- **Triggers**: Tutorial, technical, educational, computing, programming, how-to, beginner
|
||||
- **Special rules**: Visual metaphors, NO talking heads, gadget reveals, Doraemon-style characters
|
||||
- **Base**: manga + neutral + webtoon/dense
|
||||
|
||||
### wuxia
|
||||
- **Triggers**: Martial arts, wuxia, xianxia, cultivation, swordplay
|
||||
- **Special rules**: Qi effects, combat visuals, atmospheric elements
|
||||
- **Base**: ink-brush + action + splash
|
||||
|
||||
### shoujo
|
||||
- **Triggers**: Romance, love story, school life, emotional drama
|
||||
- **Special rules**: Decorative elements, eye details, romantic beats
|
||||
- **Base**: manga + romantic + standard
|
||||
|
||||
## Compatibility Matrix
|
||||
|
||||
Art Style × Tone combinations work best when matched appropriately:
|
||||
|
||||
| Art Style | ✓✓ Best | ✓ Works | ✗ Avoid |
|
||||
|-----------|---------|---------|---------|
|
||||
| ligne-claire | neutral, warm | dramatic, vintage, energetic | romantic, action |
|
||||
| manga | neutral, romantic, energetic, action | warm, dramatic | vintage |
|
||||
| realistic | neutral, warm, dramatic, vintage | action | romantic, energetic |
|
||||
| ink-brush | neutral, dramatic, action, vintage | warm | romantic, energetic |
|
||||
| chalk | neutral, warm, energetic | vintage | dramatic, action, romantic |
|
||||
|
||||
**Note**: Art Style × Tone × Layout can be freely combined. Incompatible combinations work but may produce unexpected results.
|
||||
|
||||
## Priority Order
|
||||
|
||||
1. User-specified options (`--art`, `--tone`, `--style`)
|
||||
2. EXTEND.md defaults
|
||||
3. Content signal analysis → auto-selection
|
||||
4. Fallback: ligne-claire + neutral + standard
|
||||
@@ -0,0 +1,123 @@
|
||||
# Partial Workflows
|
||||
|
||||
Options to run specific parts of the workflow.
|
||||
|
||||
## Options Summary
|
||||
|
||||
| Option | Steps Executed | Output |
|
||||
|--------|----------------|--------|
|
||||
| `--storyboard-only` | 1-3 | `storyboard.md` + `characters/` |
|
||||
| `--prompts-only` | 1-5 | + `prompts/*.md` |
|
||||
| `--images-only` | 7-9 | + images + PDF |
|
||||
| `--regenerate N` | 7 (partial) | Specific page(s) + PDF |
|
||||
|
||||
---
|
||||
|
||||
## Using `--storyboard-only`
|
||||
|
||||
Generate storyboard and characters without prompts or images:
|
||||
|
||||
```bash
|
||||
/baoyu-comic content.md --storyboard-only
|
||||
```
|
||||
|
||||
**Workflow**: Steps 1-3 only (stop after storyboard + characters)
|
||||
|
||||
**Output**:
|
||||
- `analysis.md`
|
||||
- `storyboard.md`
|
||||
- `characters/characters.md`
|
||||
|
||||
**Use case**: Review and edit the storyboard before generating images. Useful for:
|
||||
- Getting feedback on the narrative structure
|
||||
- Making manual adjustments to panel layouts
|
||||
- Defining custom characters
|
||||
|
||||
---
|
||||
|
||||
## Using `--prompts-only`
|
||||
|
||||
Generate storyboard, characters, and prompts without images:
|
||||
|
||||
```bash
|
||||
/baoyu-comic content.md --prompts-only
|
||||
```
|
||||
|
||||
**Workflow**: Steps 1-5 (generate prompts, skip images)
|
||||
|
||||
**Output**:
|
||||
- `analysis.md`
|
||||
- `storyboard.md`
|
||||
- `characters/characters.md`
|
||||
- `prompts/*.md`
|
||||
|
||||
**Use case**: Review and edit prompts before image generation. Useful for:
|
||||
- Fine-tuning image generation prompts
|
||||
- Ensuring visual consistency before committing to generation
|
||||
- Making style adjustments at the prompt level
|
||||
|
||||
---
|
||||
|
||||
## Using `--images-only`
|
||||
|
||||
Generate images from existing prompts (starts at Step 7):
|
||||
|
||||
```bash
|
||||
/baoyu-comic comic/topic-slug/ --images-only
|
||||
```
|
||||
|
||||
**Workflow**: Skip to Step 7, then 8-9
|
||||
|
||||
**Prerequisites** (must exist in directory):
|
||||
- `prompts/` directory with page prompt files
|
||||
- `storyboard.md` with style information
|
||||
- `characters/characters.md` with character definitions
|
||||
|
||||
**Output**:
|
||||
- `characters/characters.png` (if not exists)
|
||||
- `NN-{cover|page}-[slug].png` images
|
||||
- `{topic-slug}.pdf`
|
||||
|
||||
**Use case**: Re-generate images after editing prompts. Useful for:
|
||||
- Recovering from failed image generation
|
||||
- Trying different image generation settings
|
||||
- Regenerating after manual prompt edits
|
||||
|
||||
---
|
||||
|
||||
## Using `--regenerate`
|
||||
|
||||
Regenerate specific pages only:
|
||||
|
||||
```bash
|
||||
# Single page
|
||||
/baoyu-comic comic/topic-slug/ --regenerate 3
|
||||
|
||||
# Multiple pages
|
||||
/baoyu-comic comic/topic-slug/ --regenerate 2,5,8
|
||||
|
||||
# Cover page
|
||||
/baoyu-comic comic/topic-slug/ --regenerate 0
|
||||
```
|
||||
|
||||
**Workflow**:
|
||||
1. Read existing prompts for specified pages
|
||||
2. Regenerate images only for those pages
|
||||
3. Regenerate PDF
|
||||
|
||||
**Prerequisites** (must exist):
|
||||
- `prompts/NN-{cover|page}-[slug].md` for specified pages
|
||||
- `characters/characters.png` (for reference)
|
||||
|
||||
**Output**:
|
||||
- Regenerated `NN-{cover|page}-[slug].png` for specified pages
|
||||
- Updated `{topic-slug}.pdf`
|
||||
|
||||
**Use case**: Fix specific pages without regenerating entire comic. Useful for:
|
||||
- Fixing a single problematic page
|
||||
- Iterating on specific visuals
|
||||
- Regenerating pages after prompt edits
|
||||
|
||||
**Page numbering**:
|
||||
- `0` = Cover page
|
||||
- `1-N` = Content pages
|
||||
@@ -41,16 +41,18 @@ Every technical concept MUST be visualized as a metaphor:
|
||||
|
||||
### Character Roles (Required)
|
||||
|
||||
Define characters before generating:
|
||||
**DEFAULT: Use Doraemon characters** unless user explicitly specifies `--characters` or has character presets in EXTEND.md.
|
||||
|
||||
| Role | Default | Traits |
|
||||
|------|---------|--------|
|
||||
| Student (Role A) | 大雄 | Confused, asks basic but crucial questions, represents reader |
|
||||
| Mentor (Role B) | 哆啦A梦 | Knowledgeable, patient, uses gadgets as technical metaphors |
|
||||
| Challenge (Role C, optional) | 胖虎 | Represents misunderstanding, or "noise" in the data |
|
||||
| Support (Role D, optional) | 静香 | Asks clarifying questions, provides alternative perspectives |
|
||||
| Role | Default Character | Visual | Traits |
|
||||
|------|-------------------|--------|--------|
|
||||
| Student (Role A) | 大雄 (Nobita) | Boy, 10yo, round glasses, black hair, yellow shirt, navy shorts | Confused, asks basic but crucial questions, represents reader |
|
||||
| Mentor (Role B) | 哆啦A梦 (Doraemon) | Blue robot cat, white belly, 4D pocket, red nose, golden bell | Knowledgeable, patient, uses gadgets as technical metaphors |
|
||||
| Challenge (Role C) | 胖虎 (Gian) | Stocky boy, small eyes, orange shirt | Represents misunderstanding, or "noise" in the data |
|
||||
| Support (Role D) | 静香 (Shizuka) | Cute girl, black short hair, pink dress | Asks clarifying questions, provides alternative perspectives |
|
||||
|
||||
Custom characters via `--characters "Student:小明,Mentor:教授"` or EXTEND.md presets.
|
||||
**IMPORTANT**: These Doraemon characters ARE the default for ohmsha preset. Generate character definitions using these exact characters unless user requests otherwise.
|
||||
|
||||
To use custom characters: `--characters "Student:小明,Mentor:教授"` or define in EXTEND.md.
|
||||
|
||||
### Page Title Convention
|
||||
|
||||
|
||||
@@ -0,0 +1,505 @@
|
||||
# Complete Workflow
|
||||
|
||||
Full workflow for generating knowledge comics.
|
||||
|
||||
## Progress Checklist
|
||||
|
||||
Copy and track progress:
|
||||
|
||||
```
|
||||
Comic Progress:
|
||||
- [ ] Step 1: Setup & Analyze
|
||||
- [ ] 1.1 Load preferences
|
||||
- [ ] 1.2 Analyze content
|
||||
- [ ] 1.3 Check existing ⚠️ REQUIRED
|
||||
- [ ] Step 2: Confirmation 1 - Style & options ⚠️ REQUIRED
|
||||
- [ ] Step 3: Generate storyboard + characters
|
||||
- [ ] Step 4: Review outline (conditional)
|
||||
- [ ] Step 5: Generate prompts
|
||||
- [ ] Step 6: Review prompts (conditional)
|
||||
- [ ] Step 7: Generate images
|
||||
- [ ] Step 8: Merge to PDF
|
||||
- [ ] Step 9: Completion report
|
||||
```
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
Input → Preferences → Analyze → [Check Existing?] → [Confirm 1: Style + Reviews] → Storyboard → [Review Outline?] → Prompts → [Review Prompts?] → Images → PDF → Complete
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Setup & Analyze
|
||||
|
||||
### 1.1 Load Preferences (EXTEND.md)
|
||||
|
||||
Use Bash to check EXTEND.md existence (priority order):
|
||||
|
||||
```bash
|
||||
# Check project-level first
|
||||
test -f .baoyu-skills/baoyu-comic/EXTEND.md && echo "project"
|
||||
|
||||
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
|
||||
test -f "$HOME/.baoyu-skills/baoyu-comic/EXTEND.md" && echo "user"
|
||||
```
|
||||
|
||||
| Path | Location |
|
||||
|------|----------|
|
||||
| `.baoyu-skills/baoyu-comic/EXTEND.md` | Project directory |
|
||||
| `$HOME/.baoyu-skills/baoyu-comic/EXTEND.md` | User home |
|
||||
|
||||
**When EXTEND.md Found** → Read, parse, **output summary to user**:
|
||||
|
||||
```
|
||||
📋 Loaded preferences from [full path]
|
||||
├─ Watermark: [enabled/disabled] [content if enabled]
|
||||
├─ Art Style: [style name or "auto-select"]
|
||||
├─ Tone: [tone name or "auto-select"]
|
||||
├─ Layout: [layout or "auto-select"]
|
||||
├─ Language: [language or "auto-detect"]
|
||||
└─ Character presets: [count] defined
|
||||
```
|
||||
|
||||
**MUST output this summary** so user knows their current configuration. Do not skip or silently load.
|
||||
|
||||
**When EXTEND.md Not Found** → First-time setup:
|
||||
|
||||
1. Inform user: "No preferences found. Let's set up your defaults."
|
||||
2. Use AskUserQuestion to collect preferences (see `config/first-time-setup.md`)
|
||||
3. Create EXTEND.md at user-chosen location
|
||||
4. Confirm: "✓ Preferences saved to [path]"
|
||||
|
||||
**EXTEND.md Supports**: Watermark | Preferred art/tone/layout | Custom style definitions | Character presets | Language preference
|
||||
|
||||
Schema: `config/preferences-schema.md`
|
||||
|
||||
**Important**: Once EXTEND.md exists, watermark, language, and style defaults are NOT asked again in Confirmation 1 or 2. These are session-persistent settings.
|
||||
|
||||
### 1.2 Analyze Content → `analysis.md`
|
||||
|
||||
Read source content, save it if needed, and perform deep analysis.
|
||||
|
||||
**Actions**:
|
||||
1. **Save source content** (if not already a file):
|
||||
- If user provides a file path: use as-is
|
||||
- If user pastes content: save to `source.md` in target directory
|
||||
2. Read source content
|
||||
3. **Deep analysis** following `analysis-framework.md`:
|
||||
- Target audience identification
|
||||
- Value proposition for readers
|
||||
- Core themes and narrative potential
|
||||
- Key figures and their story arcs
|
||||
4. Detect source language
|
||||
5. **Determine language**:
|
||||
- If EXTEND.md has `language` → use it
|
||||
- Else if `--lang` option provided → use it
|
||||
- Else → use detected source language
|
||||
6. Determine recommended page count:
|
||||
- Short story: 5-8 pages
|
||||
- Medium complexity: 9-15 pages
|
||||
- Full biography: 16-25 pages
|
||||
7. Analyze content signals for art/tone/layout recommendations
|
||||
8. **Save to `analysis.md`**
|
||||
|
||||
**analysis.md Format**: YAML front matter (title, topic, time_span, source_language, user_language, aspect_ratio, recommended_page_count, recommended_art, recommended_tone) + sections for Target Audience, Value Proposition, Core Themes, Key Figures & Story Arcs, Content Signals, Recommended Approaches. See `analysis-framework.md` for full template.
|
||||
|
||||
### 1.3 Check Existing Content ⚠️ REQUIRED
|
||||
|
||||
**MUST execute before proceeding to Step 2.**
|
||||
|
||||
Use Bash to check if output directory exists:
|
||||
|
||||
```bash
|
||||
test -d "comic/{topic-slug}" && echo "exists"
|
||||
```
|
||||
|
||||
**If directory exists**, use AskUserQuestion:
|
||||
|
||||
```
|
||||
header: "Existing"
|
||||
question: "Existing content found. How to proceed?"
|
||||
options:
|
||||
- label: "Regenerate storyboard"
|
||||
description: "Keep images, regenerate storyboard and characters only"
|
||||
- label: "Regenerate images"
|
||||
description: "Keep storyboard, regenerate images only"
|
||||
- label: "Backup and regenerate"
|
||||
description: "Backup to {slug}-backup-{timestamp}, then regenerate all"
|
||||
- label: "Exit"
|
||||
description: "Cancel, keep existing content unchanged"
|
||||
```
|
||||
|
||||
Save result and handle accordingly:
|
||||
- **Regenerate storyboard**: Skip to Step 3, preserve `prompts/` and images
|
||||
- **Regenerate images**: Skip to Step 7, use existing prompts
|
||||
- **Backup and regenerate**: Move directory, start fresh from Step 2
|
||||
- **Exit**: End workflow immediately
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Confirmation 1 - Style & Options ⚠️
|
||||
|
||||
**Purpose**: Select visual style + decide whether to review outline before generation. **Do NOT skip.**
|
||||
|
||||
**Note**: Watermark and language already configured in EXTEND.md (Step 1).
|
||||
|
||||
**Display summary**:
|
||||
- Content type + topic identified
|
||||
- Key figures extracted
|
||||
- Time span detected
|
||||
- Recommended page count
|
||||
- Language: [from EXTEND.md or detected]
|
||||
- **Recommended style**: [art] + [tone] (based on content signals)
|
||||
|
||||
**Use AskUserQuestion** for:
|
||||
|
||||
### Question 1: Visual Style
|
||||
|
||||
If a preset is recommended (see `auto-selection.md`), show it first:
|
||||
|
||||
```
|
||||
header: "Style"
|
||||
question: "Which visual style for this comic?"
|
||||
options:
|
||||
- label: "[preset name] preset (Recommended)" # If preset recommended
|
||||
description: "[preset description] - includes special rules"
|
||||
- label: "[recommended art] + [recommended tone] (Recommended)" # If no preset
|
||||
description: "Best match for your content based on analysis"
|
||||
- label: "ligne-claire + neutral"
|
||||
description: "Classic educational, Logicomix style"
|
||||
- label: "ohmsha preset"
|
||||
description: "Educational manga with visual metaphors, gadgets, NO talking heads"
|
||||
- label: "Custom"
|
||||
description: "Specify your own art + tone or preset"
|
||||
```
|
||||
|
||||
**Preset vs Art+Tone**: Presets include special rules beyond art+tone. `ohmsha` = manga + neutral + visual metaphor rules + character roles + NO talking heads. Plain `manga + neutral` does NOT include these rules.
|
||||
|
||||
### Question 2: Narrative Focus (multiSelect: true)
|
||||
|
||||
```
|
||||
header: "Focus"
|
||||
question: "What should the comic emphasize? (Select all that apply)"
|
||||
options:
|
||||
- label: "Biography/life story"
|
||||
description: "Follow a person's journey through key life events"
|
||||
- label: "Concept explanation"
|
||||
description: "Break down complex ideas visually"
|
||||
- label: "Historical event"
|
||||
description: "Dramatize important historical moments"
|
||||
- label: "Tutorial/how-to"
|
||||
description: "Step-by-step educational guide"
|
||||
```
|
||||
|
||||
### Question 3: Target Audience
|
||||
|
||||
```
|
||||
header: "Audience"
|
||||
question: "Who is the primary reader?"
|
||||
options:
|
||||
- label: "General readers"
|
||||
description: "Broad appeal, accessible content"
|
||||
- label: "Students/learners"
|
||||
description: "Educational focus, clear explanations"
|
||||
- label: "Industry professionals"
|
||||
description: "Technical depth, domain knowledge"
|
||||
- label: "Children/young readers"
|
||||
description: "Simplified language, engaging visuals"
|
||||
```
|
||||
|
||||
### Question 4: Outline Review
|
||||
|
||||
```
|
||||
header: "Review"
|
||||
question: "Do you want to review the outline before image generation?"
|
||||
options:
|
||||
- label: "Yes, let me review (Recommended)"
|
||||
description: "Review storyboard and characters before generating images"
|
||||
- label: "No, generate directly"
|
||||
description: "Skip outline review, start generating immediately"
|
||||
```
|
||||
|
||||
### Question 5: Prompt Review
|
||||
|
||||
```
|
||||
header: "Prompts"
|
||||
question: "Review prompts before generating images?"
|
||||
options:
|
||||
- label: "Yes, review prompts (Recommended)"
|
||||
description: "Review image generation prompts before generating"
|
||||
- label: "No, skip prompt review"
|
||||
description: "Proceed directly to image generation"
|
||||
```
|
||||
|
||||
**After response**:
|
||||
1. Update `analysis.md` with user preferences
|
||||
2. **Store `skip_outline_review`** flag based on Question 4 response
|
||||
3. **Store `skip_prompt_review`** flag based on Question 5 response
|
||||
4. → Step 3
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Generate Storyboard + Characters
|
||||
|
||||
Create storyboard and character definitions using the confirmed style from Step 2.
|
||||
|
||||
**Loading Style References**:
|
||||
- Art style: `art-styles/{art}.md`
|
||||
- Tone: `tones/{tone}.md`
|
||||
- If preset (ohmsha/wuxia/shoujo): also load `presets/{preset}.md`
|
||||
|
||||
**Generate**:
|
||||
|
||||
1. **Storyboard** (`storyboard.md`):
|
||||
- YAML front matter with art_style, tone, layout, aspect_ratio
|
||||
- Cover design
|
||||
- Each page: layout, panel breakdown, visual prompts
|
||||
- **Written in user's preferred language** (from Step 1)
|
||||
- Reference: `storyboard-template.md`
|
||||
- **If using preset**: Load and apply preset rules from `presets/`
|
||||
|
||||
2. **Character definitions** (`characters/characters.md`):
|
||||
- Visual specs matching the art style (in user's preferred language)
|
||||
- Include Reference Sheet Prompt for later image generation
|
||||
- Reference: `character-template.md`
|
||||
- **If using ohmsha preset**: Use default Doraemon characters (see below)
|
||||
|
||||
**Ohmsha Default Characters** (use these unless user specifies `--characters`):
|
||||
|
||||
| Role | Character | Visual Description |
|
||||
|------|-----------|-------------------|
|
||||
| Student | 大雄 (Nobita) | Japanese boy, 10yo, round glasses, black hair parted in middle, yellow shirt, navy shorts |
|
||||
| Mentor | 哆啦A梦 (Doraemon) | Round blue robot cat, big white eyes, red nose, whiskers, white belly with 4D pocket, golden bell, no ears |
|
||||
| Challenge | 胖虎 (Gian) | Stocky boy, rough features, small eyes, orange shirt |
|
||||
| Support | 静香 (Shizuka) | Cute girl, black short hair, pink dress, gentle expression |
|
||||
|
||||
These are the canonical ohmsha-style characters. Do NOT create custom characters for ohmsha unless explicitly requested.
|
||||
|
||||
**After generation**:
|
||||
- If `skip_outline_review` is true → Skip Step 4, go directly to Step 5
|
||||
- If `skip_outline_review` is false → Continue to Step 4
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Review Outline (Conditional)
|
||||
|
||||
**Skip this step** if user selected "No, generate directly" in Step 2.
|
||||
|
||||
**Purpose**: User reviews and confirms storyboard + characters before generation.
|
||||
|
||||
**Display**:
|
||||
- Page count and structure
|
||||
- Art style + Tone combination
|
||||
- Page-by-page summary (Cover → P1 → P2...)
|
||||
- Character list with brief descriptions
|
||||
|
||||
**Use AskUserQuestion**:
|
||||
|
||||
```
|
||||
header: "Confirm"
|
||||
question: "Ready to generate images with this outline?"
|
||||
options:
|
||||
- label: "Yes, proceed (Recommended)"
|
||||
description: "Generate character sheet and comic pages"
|
||||
- label: "Edit storyboard first"
|
||||
description: "I'll modify storyboard.md before continuing"
|
||||
- label: "Edit characters first"
|
||||
description: "I'll modify characters/characters.md before continuing"
|
||||
- label: "Edit both"
|
||||
description: "I'll modify both files before continuing"
|
||||
```
|
||||
|
||||
**After response**:
|
||||
1. If user wants to edit → Wait for user to finish editing, then ask again
|
||||
2. If user confirms → Continue to Step 5
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Generate Prompts
|
||||
|
||||
Create image generation prompts for all pages.
|
||||
|
||||
**Style Reference Loading**:
|
||||
- Read `art-styles/{art}.md` for rendering guidelines
|
||||
- Read `tones/{tone}.md` for mood/color adjustments
|
||||
- If preset: Read `presets/{preset}.md` for special rules
|
||||
|
||||
**For each page (cover + pages)**:
|
||||
1. Create prompt following art style + tone guidelines
|
||||
2. Include character visual descriptions for consistency
|
||||
3. Save to `prompts/NN-{cover|page}-[slug].md`
|
||||
|
||||
**Prompt File Format**:
|
||||
```markdown
|
||||
# Page NN: [Title]
|
||||
|
||||
## Visual Style
|
||||
Art: [art style] | Tone: [tone] | Layout: [layout type]
|
||||
|
||||
## Character Reference
|
||||
[Character descriptions from characters/characters.md]
|
||||
|
||||
## Panel Breakdown
|
||||
[From storyboard.md - panel descriptions, actions, dialogue]
|
||||
|
||||
## Generation Prompt
|
||||
[Combined prompt for image generation skill]
|
||||
```
|
||||
|
||||
**Watermark Application** (if enabled in preferences):
|
||||
Add to each prompt:
|
||||
```
|
||||
Include a subtle watermark "[content]" positioned at [position]
|
||||
with approximately [opacity*100]% visibility. The watermark should
|
||||
be legible but not distracting from the comic panels and storytelling.
|
||||
Ensure watermark does not overlap speech bubbles or key action.
|
||||
```
|
||||
Reference: `config/watermark-guide.md`
|
||||
|
||||
**After generation**:
|
||||
- If `skip_prompt_review` is true → Skip Step 6, go directly to Step 7
|
||||
- If `skip_prompt_review` is false → Continue to Step 6
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Review Prompts (Conditional)
|
||||
|
||||
**Skip this step** if user selected "No, skip prompt review" in Step 2.
|
||||
|
||||
**Purpose**: User reviews and confirms prompts before image generation.
|
||||
|
||||
**Display prompt summary table**:
|
||||
|
||||
| Page | Title | Key Elements |
|
||||
|------|-------|--------------|
|
||||
| Cover | [title] | [main visual] |
|
||||
| P1 | [title] | [key elements] |
|
||||
| ... | ... | ... |
|
||||
|
||||
**Use AskUserQuestion**:
|
||||
|
||||
```
|
||||
header: "Confirm"
|
||||
question: "Ready to generate images with these prompts?"
|
||||
options:
|
||||
- label: "Yes, proceed (Recommended)"
|
||||
description: "Generate all comic page images"
|
||||
- label: "Edit prompts first"
|
||||
description: "I'll modify prompts/*.md before continuing"
|
||||
- label: "Regenerate prompts"
|
||||
description: "Regenerate all prompts with different approach"
|
||||
```
|
||||
|
||||
**After response**:
|
||||
1. If user wants to edit → Wait for user to finish editing, then ask again
|
||||
2. If user wants to regenerate → Go back to Step 5
|
||||
3. If user confirms → Continue to Step 7
|
||||
|
||||
---
|
||||
|
||||
## Step 7: Generate Images
|
||||
|
||||
With confirmed prompts from Step 5/6:
|
||||
|
||||
### 7.1 Generate Character Reference Sheet (first)
|
||||
|
||||
1. Use Reference Sheet Prompt from `characters/characters.md`
|
||||
2. Generate → `characters/characters.png`
|
||||
3. This ensures visual consistency for all subsequent pages
|
||||
|
||||
### 7.2 Generate Comic Pages
|
||||
|
||||
**CRITICAL: Character Reference is MANDATORY** for visual consistency across all pages.
|
||||
|
||||
**Before generating any page**:
|
||||
1. Read the image generation skill's SKILL.md
|
||||
2. Check if it supports reference image input (`--ref`, `--reference`, etc.)
|
||||
3. Choose the appropriate strategy below
|
||||
|
||||
**Character Reference Strategy**:
|
||||
|
||||
| Skill Capability | Strategy | Action |
|
||||
|------------------|----------|--------|
|
||||
| Supports `--ref` | **Strategy A** | Pass `characters/characters.png` with EVERY page |
|
||||
| Does NOT support `--ref` | **Strategy B** | Prepend character descriptions to EVERY prompt |
|
||||
|
||||
**Strategy A: Using `--ref` parameter** (e.g., baoyu-image-gen)
|
||||
|
||||
```bash
|
||||
# Each page generation MUST include --ref
|
||||
npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
|
||||
--promptfiles prompts/01-page-xxx.md \
|
||||
--image 01-page-xxx.png \
|
||||
--ar 3:4 \
|
||||
--ref characters/characters.png
|
||||
```
|
||||
|
||||
**Strategy B: Embedding character descriptions in prompt**
|
||||
|
||||
When skill does NOT support reference images, create combined prompt files:
|
||||
|
||||
```markdown
|
||||
# prompts/01-page-xxx.md (with embedded character reference)
|
||||
|
||||
## Character Reference (maintain consistency)
|
||||
[Copy relevant sections from characters/characters.md here]
|
||||
- 大雄: Japanese boy, round glasses, yellow shirt, navy shorts...
|
||||
- 哆啦A梦: Round blue robot cat, white belly, red nose, golden bell...
|
||||
|
||||
## Page Content
|
||||
[Original page prompt here]
|
||||
```
|
||||
|
||||
**For each page (cover + pages)**:
|
||||
1. Read prompt from `prompts/NN-{cover|page}-[slug].md`
|
||||
2. Generate image using Strategy A or B (based on skill capability)
|
||||
3. Save to `NN-{cover|page}-[slug].png`
|
||||
4. Report progress after each generation: "Generated X/N: [page title]"
|
||||
|
||||
**Session Management**:
|
||||
If image generation skill supports `--sessionId`:
|
||||
1. Generate unique session ID: `comic-{topic-slug}-{timestamp}`
|
||||
2. Use same session ID for all pages
|
||||
3. Ensures visual consistency across generated images
|
||||
|
||||
---
|
||||
|
||||
## Step 8: Merge to PDF
|
||||
|
||||
After all images generated:
|
||||
|
||||
```bash
|
||||
npx -y bun ${SKILL_DIR}/scripts/merge-to-pdf.ts <comic-dir>
|
||||
```
|
||||
|
||||
Creates `{topic-slug}.pdf` with all pages as full-page images.
|
||||
|
||||
---
|
||||
|
||||
## Step 9: Completion Report
|
||||
|
||||
```
|
||||
Comic Complete!
|
||||
Title: [title] | Art: [art] | Tone: [tone] | Pages: [count] | Aspect: [ratio] | Language: [lang]
|
||||
Watermark: [enabled/disabled]
|
||||
Location: [path]
|
||||
✓ analysis.md
|
||||
✓ characters.png
|
||||
✓ 00-cover-[slug].png ... NN-page-[slug].png
|
||||
✓ {topic-slug}.pdf
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Page Modification
|
||||
|
||||
| Action | Steps |
|
||||
|--------|-------|
|
||||
| **Edit** | Update prompt → Regenerate image → Regenerate PDF |
|
||||
| **Add** | Create prompt at position → Generate image → Renumber subsequent (NN+1) → Update storyboard → Regenerate PDF |
|
||||
| **Delete** | Remove files → Renumber subsequent (NN-1) → Update storyboard → Regenerate PDF |
|
||||
|
||||
**File naming**: `NN-{cover|page}-[slug].png` (e.g., `03-page-enigma-machine.png`)
|
||||
- Slugs: kebab-case, unique, derived from content
|
||||
- Renumbering: Update NN prefix only, slugs unchanged
|
||||
@@ -1,23 +1,24 @@
|
||||
---
|
||||
name: baoyu-cover-image
|
||||
description: Generates article cover images with 20 hand-drawn styles and auto-style selection. Supports cinematic (2.35:1), widescreen (16:9), and square (1:1) aspects. Use when user asks to "generate cover image", "create article cover", "make cover", or mentions "封面图".
|
||||
description: Generates article cover images with 4 dimensions (type, style, text, mood) and 20 hand-drawn styles. Supports cinematic (2.35:1), widescreen (16:9), and square (1:1) aspects. Use when user asks to "generate cover image", "create article cover", "make cover", or mentions "封面图".
|
||||
---
|
||||
|
||||
# Cover Image Generator
|
||||
|
||||
Generate elegant cover images for articles with multiple style options.
|
||||
Generate elegant cover images for articles with 4-dimensional customization.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Auto-select style and aspect based on content
|
||||
# Auto-select all dimensions based on content
|
||||
/baoyu-cover-image path/to/article.md
|
||||
|
||||
# Specify style
|
||||
/baoyu-cover-image article.md --style blueprint
|
||||
# Quick mode: skip confirmation, use auto-selection
|
||||
/baoyu-cover-image article.md --quick
|
||||
|
||||
# Specify aspect ratio
|
||||
/baoyu-cover-image article.md --aspect 16:9
|
||||
# Specify dimensions
|
||||
/baoyu-cover-image article.md --type conceptual --style blueprint
|
||||
/baoyu-cover-image article.md --text title-subtitle --mood bold
|
||||
|
||||
# Visual only (no title text)
|
||||
/baoyu-cover-image article.md --no-title
|
||||
@@ -27,7 +28,7 @@ Generate elegant cover images for articles with multiple style options.
|
||||
[paste content]
|
||||
|
||||
# Direct input with options
|
||||
/baoyu-cover-image --style notion --aspect 1:1
|
||||
/baoyu-cover-image --style notion --aspect 1:1 --quick
|
||||
[paste content]
|
||||
```
|
||||
|
||||
@@ -35,20 +36,25 @@ Generate elegant cover images for articles with multiple style options.
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--type <name>` | Cover type (see Type Gallery) |
|
||||
| `--type <name>` | Cover type: hero, conceptual, typography, metaphor, scene, minimal |
|
||||
| `--style <name>` | Cover style (see Style Gallery) |
|
||||
| `--text <level>` | Text density: none, title-only, title-subtitle, text-rich |
|
||||
| `--mood <level>` | Emotional intensity: subtle, balanced, bold |
|
||||
| `--aspect <ratio>` | 2.35:1 (default), 16:9, 1:1 |
|
||||
| `--lang <code>` | Title language (en, zh, ja, etc.) |
|
||||
| `--no-title` | Visual only, no title text |
|
||||
| `--no-title` | Alias for `--text none` |
|
||||
| `--quick` | Skip confirmation, use auto-selection for missing dimensions |
|
||||
|
||||
## Two Dimensions
|
||||
## Four Dimensions
|
||||
|
||||
| Dimension | Controls | Examples |
|
||||
|-----------|----------|----------|
|
||||
| **Type** | Visual composition, information structure | hero, conceptual, typography, metaphor, scene, minimal |
|
||||
| **Style** | Visual aesthetics, colors, mood | elegant, blueprint, notion, warm, minimal, watercolor |
|
||||
| Dimension | Controls | Values | Default |
|
||||
|-----------|----------|--------|---------|
|
||||
| **Type** | Visual composition, information structure | hero, conceptual, typography, metaphor, scene, minimal | auto |
|
||||
| **Style** | Visual aesthetics, colors, technique | 20 built-in styles | auto |
|
||||
| **Text** | Text density, information hierarchy | none, title-only, title-subtitle, text-rich | title-only |
|
||||
| **Mood** | Emotional intensity, visual weight | subtle, balanced, bold | balanced |
|
||||
|
||||
Type × Style can be freely combined. Example: `--type conceptual --style blueprint` creates technical concept visualization with schematic aesthetics.
|
||||
Dimensions can be freely combined. Example: `--type conceptual --style blueprint --text title-only --mood subtle` creates a calm technical concept visualization.
|
||||
|
||||
## Type Gallery
|
||||
|
||||
@@ -101,19 +107,6 @@ When `--type` is omitted, select based on content signals:
|
||||
|
||||
Style definitions: [references/styles/](references/styles/)
|
||||
|
||||
## Type × Style Compatibility
|
||||
|
||||
| | elegant | blueprint | notion | warm | minimal | watercolor | bold-editorial | dark-atmospheric |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| hero | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
|
||||
| conceptual | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✗ | ✓ | ✓ |
|
||||
| typography | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ |
|
||||
| metaphor | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ |
|
||||
| scene | ✓ | ✗ | ✗ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ |
|
||||
| minimal | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✗ | ✓ |
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
|
||||
## Auto Style Selection
|
||||
|
||||
When `--style` is omitted, select based on content signals:
|
||||
@@ -140,6 +133,89 @@ When `--style` is omitted, select based on content signals:
|
||||
| Lifestyle, travel | `watercolor` |
|
||||
| Business, professional | `elegant` |
|
||||
|
||||
## Text Dimension
|
||||
|
||||
| Value | Title | Subtitle | Tags | Use Case |
|
||||
|-------|:-----:|:--------:|:----:|----------|
|
||||
| `none` | - | - | - | Pure visual, no text |
|
||||
| `title-only` | ✓ (≤8字) | - | - | Simple headline (default) |
|
||||
| `title-subtitle` | ✓ | ✓ (≤15字) | - | Title + supporting context |
|
||||
| `text-rich` | ✓ | ✓ | ✓ (2-4) | Information-dense |
|
||||
|
||||
Full guide: [references/dimensions/text.md](references/dimensions/text.md)
|
||||
|
||||
## Auto Text Selection
|
||||
|
||||
When `--text` is omitted, select based on content signals:
|
||||
|
||||
| Signals | Text Level |
|
||||
|---------|------------|
|
||||
| Visual-only, photography, abstract, art | `none` |
|
||||
| Article, blog, standard cover | `title-only` |
|
||||
| Series, tutorial, technical with context | `title-subtitle` |
|
||||
| Announcement, features, multiple points, infographic | `text-rich` |
|
||||
|
||||
Default: `title-only`
|
||||
|
||||
## Mood Dimension
|
||||
|
||||
| Value | Contrast | Saturation | Weight | Use Case |
|
||||
|-------|:--------:|:----------:|:------:|----------|
|
||||
| `subtle` | Low | Muted | Light | Corporate, thought leadership |
|
||||
| `balanced` | Medium | Normal | Medium | General articles (default) |
|
||||
| `bold` | High | Vivid | Heavy | Announcements, promotions |
|
||||
|
||||
Full guide: [references/dimensions/mood.md](references/dimensions/mood.md)
|
||||
|
||||
## Auto Mood Selection
|
||||
|
||||
When `--mood` is omitted, select based on content signals:
|
||||
|
||||
| Signals | Mood Level |
|
||||
|---------|------------|
|
||||
| Professional, corporate, thought leadership, academic, luxury | `subtle` |
|
||||
| General, educational, standard, blog, documentation | `balanced` |
|
||||
| Launch, announcement, promotion, event, gaming, entertainment | `bold` |
|
||||
|
||||
Default: `balanced`
|
||||
|
||||
## Compatibility Matrices
|
||||
|
||||
### Type × Style
|
||||
|
||||
| | elegant | blueprint | notion | warm | minimal | watercolor | bold-editorial | dark-atmospheric |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| hero | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
|
||||
| conceptual | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✗ | ✓ | ✓ |
|
||||
| typography | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ |
|
||||
| metaphor | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ |
|
||||
| scene | ✓ | ✗ | ✗ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ |
|
||||
| minimal | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✗ | ✓ |
|
||||
|
||||
### Type × Text
|
||||
|
||||
| | none | title-only | title-subtitle | text-rich |
|
||||
|---|:---:|:---:|:---:|:---:|
|
||||
| hero | ✓ | ✓✓ | ✓✓ | ✓ |
|
||||
| conceptual | ✓✓ | ✓✓ | ✓ | ✓ |
|
||||
| typography | ✗ | ✓ | ✓✓ | ✓✓ |
|
||||
| metaphor | ✓✓ | ✓ | ✓ | ✗ |
|
||||
| scene | ✓✓ | ✓ | ✓ | ✗ |
|
||||
| minimal | ✓✓ | ✓✓ | ✓ | ✗ |
|
||||
|
||||
### Type × Mood
|
||||
|
||||
| | subtle | balanced | bold |
|
||||
|---|:---:|:---:|:---:|
|
||||
| hero | ✓ | ✓✓ | ✓✓ |
|
||||
| conceptual | ✓✓ | ✓✓ | ✓ |
|
||||
| typography | ✓ | ✓✓ | ✓✓ |
|
||||
| metaphor | ✓✓ | ✓✓ | ✓ |
|
||||
| scene | ✓✓ | ✓✓ | ✓ |
|
||||
| minimal | ✓✓ | ✓✓ | ✗ |
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
|
||||
## File Structure
|
||||
|
||||
Each session creates an independent directory named by content slug:
|
||||
@@ -175,7 +251,7 @@ Copy and track progress:
|
||||
Cover Image Progress:
|
||||
- [ ] Step 0: Check preferences (EXTEND.md) ⚠️ REQUIRED if not found
|
||||
- [ ] Step 1: Analyze content
|
||||
- [ ] Step 2: Confirm options (type + style + aspect) ⚠️ REQUIRED
|
||||
- [ ] Step 2: Confirm options (4 dimensions) ⚠️ REQUIRED unless --quick or all specified
|
||||
- [ ] Step 3: Create prompt
|
||||
- [ ] Step 4: Generate image
|
||||
- [ ] Step 5: Completion report
|
||||
@@ -184,7 +260,9 @@ Cover Image Progress:
|
||||
### Flow
|
||||
|
||||
```
|
||||
Input → [Step 0: Preferences/Setup] → Analyze → [Confirm: Type + Style + Aspect] → Prompt → Generate → Complete
|
||||
Input → [Step 0: Preferences/Setup] → Analyze → [Confirm: 4 Dimensions] → Prompt → Generate → Complete
|
||||
↓
|
||||
(skip if --quick or all dimensions specified)
|
||||
```
|
||||
|
||||
### Step 0: Load Preferences (EXTEND.md) ⚠️
|
||||
@@ -226,7 +304,10 @@ Preferences loaded from [project/user]:
|
||||
• Watermark: [enabled/disabled] [content if enabled]
|
||||
• Type: [preferred_type or "auto"]
|
||||
• Style: [preferred_style or "auto"]
|
||||
• Text: [preferred_text or "title-only"]
|
||||
• Mood: [preferred_mood or "balanced"]
|
||||
• Aspect: [default_aspect]
|
||||
• Quick mode: [enabled/disabled]
|
||||
• Language: [language or "auto"]
|
||||
```
|
||||
|
||||
@@ -284,7 +365,18 @@ options:
|
||||
description: "Square, social media friendly"
|
||||
```
|
||||
|
||||
**Q5: Save Location**
|
||||
**Q5: Quick Mode**
|
||||
```yaml
|
||||
header: "Quick"
|
||||
question: "Enable quick mode by default?"
|
||||
options:
|
||||
- label: "No (Recommended)"
|
||||
description: "Confirm dimension choices each time"
|
||||
- label: "Yes"
|
||||
description: "Skip confirmation, use auto-selection"
|
||||
```
|
||||
|
||||
**Q6: Save Location**
|
||||
```yaml
|
||||
header: "Save"
|
||||
question: "Where to save preferences?"
|
||||
@@ -299,7 +391,7 @@ options:
|
||||
|
||||
Full setup details: `references/config/first-time-setup.md`
|
||||
|
||||
**EXTEND.md Supports**: Watermark | Preferred type | Preferred style | Default aspect ratio | Custom style definitions | Language preference
|
||||
**EXTEND.md Supports**: Watermark | Preferred type | Preferred style | Preferred text | Preferred mood | Default aspect ratio | Quick mode | Custom style definitions | Language preference
|
||||
|
||||
Schema: `references/config/preferences-schema.md`
|
||||
|
||||
@@ -323,7 +415,30 @@ Read source content, save it if needed, and perform analysis.
|
||||
|
||||
### Step 2: Confirm Options ⚠️
|
||||
|
||||
**Purpose**: Validate type, style and aspect ratio. **Do NOT skip.**
|
||||
**Purpose**: Validate all 4 dimensions. **Skip if `--quick` flag OR all 4 dimensions specified via CLI.**
|
||||
|
||||
**Skip Conditions**:
|
||||
| Condition | Behavior |
|
||||
|-----------|----------|
|
||||
| `--quick` flag | Auto-select missing dimensions, skip to Step 3 |
|
||||
| All 4 dimensions specified | Use specified values, skip to Step 3 |
|
||||
| `quick_mode: true` in EXTEND.md | Auto-select missing dimensions, skip to Step 3 |
|
||||
| Otherwise | Show confirmation (current behavior) |
|
||||
|
||||
**Quick Mode Output** (when skipping confirmation):
|
||||
|
||||
```
|
||||
Quick Mode: Auto-selected dimensions
|
||||
• Type: [type] ([reason])
|
||||
• Style: [style] ([reason])
|
||||
• Text: [text] ([reason])
|
||||
• Mood: [mood] ([reason])
|
||||
• Aspect: [aspect]
|
||||
|
||||
Generating...
|
||||
```
|
||||
|
||||
**Confirmation Flow** (when NOT skipping):
|
||||
|
||||
**Language**: Auto-determined (user's input language > saved preference > source language). No need to ask.
|
||||
|
||||
@@ -364,7 +479,41 @@ options:
|
||||
description: "[reason]"
|
||||
```
|
||||
|
||||
**Question 3: Aspect Ratio** (if not specified via `--aspect`)
|
||||
**Question 3: Text** (if not specified via `--text`)
|
||||
- Based on selected Type, show compatible text levels (✓✓ first from compatibility matrix)
|
||||
|
||||
```yaml
|
||||
header: "Text"
|
||||
question: "Text density level?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "title-only (Recommended)"
|
||||
description: "Simple headline, ≤8 characters"
|
||||
- label: "none"
|
||||
description: "Pure visual, no text elements"
|
||||
- label: "title-subtitle"
|
||||
description: "Title + supporting context"
|
||||
- label: "text-rich"
|
||||
description: "Title + subtitle + keyword tags"
|
||||
```
|
||||
|
||||
**Question 4: Mood** (if not specified via `--mood`)
|
||||
- Based on content analysis, show recommended mood
|
||||
|
||||
```yaml
|
||||
header: "Mood"
|
||||
question: "Emotional intensity?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "balanced (Recommended)"
|
||||
description: "Medium contrast and saturation, versatile"
|
||||
- label: "subtle"
|
||||
description: "Low contrast, muted colors, calm"
|
||||
- label: "bold"
|
||||
description: "High contrast, vivid colors, dynamic"
|
||||
```
|
||||
|
||||
**Question 5: Aspect Ratio** (if not specified via `--aspect`)
|
||||
|
||||
```yaml
|
||||
header: "Aspect"
|
||||
@@ -379,35 +528,61 @@ options:
|
||||
description: "Square, social media friendly"
|
||||
```
|
||||
|
||||
**After response**: Proceed to Step 3 with confirmed type + style + aspect ratio.
|
||||
**After response**: Proceed to Step 3 with confirmed dimensions.
|
||||
|
||||
### Step 3: Create Prompt
|
||||
|
||||
Save to `prompts/cover.md`:
|
||||
|
||||
```markdown
|
||||
Cover theme: [2-3 words]
|
||||
# Content Context
|
||||
Article title: [full original title from source]
|
||||
Content summary: [2-3 sentence summary of key points and themes]
|
||||
Keywords: [5-8 key terms extracted from content]
|
||||
|
||||
# Visual Design
|
||||
Cover theme: [2-3 words visual interpretation]
|
||||
Type: [confirmed type]
|
||||
Style: [confirmed style]
|
||||
Text level: [confirmed text level]
|
||||
Mood: [confirmed mood]
|
||||
Aspect ratio: [confirmed ratio]
|
||||
Title text: [max 8 chars, or "none" if --no-title]
|
||||
Language: [confirmed language]
|
||||
|
||||
# Text Elements
|
||||
[Based on text level:]
|
||||
- none: "No text elements"
|
||||
- title-only: "Title: [max 8 chars headline]"
|
||||
- title-subtitle: "Title: [headline] / Subtitle: [max 15 chars context]"
|
||||
- text-rich: "Title: [headline] / Subtitle: [context] / Tags: [2-4 keywords]"
|
||||
|
||||
# Mood Application
|
||||
[Based on mood level:]
|
||||
- subtle: "Use low contrast, muted colors, light visual weight, calm aesthetic"
|
||||
- balanced: "Use medium contrast, normal saturation, balanced visual weight"
|
||||
- bold: "Use high contrast, vivid saturated colors, heavy visual weight, dynamic energy"
|
||||
|
||||
# Composition
|
||||
Type composition:
|
||||
- [Type-specific layout and structure]
|
||||
|
||||
Visual composition:
|
||||
- Main visual: [type + style appropriate metaphor]
|
||||
- Main visual: [metaphor derived from content meaning]
|
||||
- Layout: [positioning based on type and aspect ratio]
|
||||
- Decorative: [style elements]
|
||||
- Decorative: [style elements that reinforce content theme]
|
||||
|
||||
Color scheme: [primary, background, accent from style]
|
||||
Color scheme: [primary, background, accent from style, adjusted by mood]
|
||||
Type notes: [key characteristics from type definition]
|
||||
Style notes: [key characteristics from style definition]
|
||||
|
||||
[Watermark section if enabled]
|
||||
```
|
||||
|
||||
**Content-Driven Design**:
|
||||
- Article title and summary inform the visual metaphor choice
|
||||
- Keywords guide decorative elements and symbols
|
||||
- The skill controls visual style; the content drives meaning
|
||||
|
||||
**Type-Specific Composition**:
|
||||
|
||||
| Type | Composition Guidelines |
|
||||
@@ -419,7 +594,7 @@ Style notes: [key characteristics from style definition]
|
||||
| `scene` | Atmospheric environment, narrative elements, mood-setting lighting and colors |
|
||||
| `minimal` | Single focal element, generous whitespace (60%+), essential shapes only |
|
||||
|
||||
**Title guidelines** (if included):
|
||||
**Title guidelines** (when text level includes title):
|
||||
- Max 8 characters, punchy headline
|
||||
- Use hooks: numbers, questions, contrasts
|
||||
- Match confirmed language
|
||||
@@ -435,6 +610,10 @@ Reference: `references/config/watermark-guide.md`
|
||||
|
||||
### Step 4: Generate Image
|
||||
|
||||
**Backup Existing Cover** (if regenerating):
|
||||
If `cover.png` already exists in the output directory:
|
||||
- Rename to `cover-backup-YYYYMMDD-HHMMSS.png`
|
||||
|
||||
**Image Generation Skill Selection**:
|
||||
1. Check available image generation skills
|
||||
2. If multiple skills available, ask user preference
|
||||
@@ -453,8 +632,10 @@ Cover Generated!
|
||||
Topic: [topic]
|
||||
Type: [type name]
|
||||
Style: [style name]
|
||||
Text: [text level]
|
||||
Mood: [mood level]
|
||||
Aspect: [ratio]
|
||||
Title: [title or "visual only"]
|
||||
Title: [title text or "visual only"]
|
||||
Language: [lang]
|
||||
Watermark: [enabled/disabled]
|
||||
Location: [directory path]
|
||||
@@ -463,29 +644,39 @@ Files:
|
||||
✓ source-{slug}.{ext}
|
||||
✓ prompts/cover.md
|
||||
✓ cover.png
|
||||
[✓ cover-backup-{timestamp}.png (if regenerated)]
|
||||
```
|
||||
|
||||
## Image Modification
|
||||
|
||||
| Action | Steps |
|
||||
|--------|-------|
|
||||
| **Regenerate** | Update prompt → Regenerate with same settings |
|
||||
| **Change type** | Confirm new type → Update prompt → Regenerate |
|
||||
| **Change style** | Confirm new style → Update prompt → Regenerate |
|
||||
| **Change aspect** | Confirm new aspect → Update prompt → Regenerate |
|
||||
| **Regenerate** | Backup existing → Update prompt → Regenerate with same settings |
|
||||
| **Change type** | Backup existing → Confirm new type → Update prompt → Regenerate |
|
||||
| **Change style** | Backup existing → Confirm new style → Update prompt → Regenerate |
|
||||
| **Change text** | Backup existing → Confirm new text level → Update prompt → Regenerate |
|
||||
| **Change mood** | Backup existing → Confirm new mood → Update prompt → Regenerate |
|
||||
| **Change aspect** | Backup existing → Confirm new aspect → Update prompt → Regenerate |
|
||||
|
||||
All modifications automatically backup the existing `cover.png` before regenerating.
|
||||
|
||||
## Notes
|
||||
|
||||
- Cover must be readable at small preview sizes
|
||||
- Visual metaphors > literal representations
|
||||
- Title: max 8 chars, readable, impactful
|
||||
- **Two confirmation points**: Step 0 (first-time setup if no EXTEND.md) + Step 2 (options) - do NOT skip
|
||||
- **Two confirmation points**: Step 0 (first-time setup if no EXTEND.md) + Step 2 (options) - can skip Step 2 with `--quick`
|
||||
- Use confirmed language for title text
|
||||
- Maintain watermark consistency if enabled
|
||||
- Check Type × Style compatibility matrix when selecting combinations
|
||||
- Check compatibility matrices when selecting combinations
|
||||
- `--no-title` is preserved as alias for `--text none`
|
||||
|
||||
## References
|
||||
|
||||
**Dimensions**:
|
||||
- `references/dimensions/text.md` - Text density dimension
|
||||
- `references/dimensions/mood.md` - Mood intensity dimension
|
||||
|
||||
**Styles**: `references/styles/<name>.md` - Style definitions
|
||||
|
||||
**Config**:
|
||||
|
||||
@@ -13,14 +13,49 @@ Create a WeChat article cover image following these guidelines:
|
||||
- Ample whitespace, highlight core message, avoid cluttered layouts
|
||||
- Main visual elements centered or slightly left (leave right side for title area if title included)
|
||||
|
||||
## Four Dimensions
|
||||
|
||||
### Type (Visual Composition)
|
||||
- `hero`: Large focal visual (60-70% area), dramatic composition
|
||||
- `conceptual`: Abstract shapes, information hierarchy, clean zones
|
||||
- `typography`: Title as primary element (40%+ area), minimal visuals
|
||||
- `metaphor`: Concrete object representing abstract idea, symbolic elements
|
||||
- `scene`: Atmospheric environment, narrative elements, mood lighting
|
||||
- `minimal`: Single focal element, generous whitespace (60%+)
|
||||
|
||||
### Style (Visual Aesthetics)
|
||||
Apply the specified style's color palette, visual elements, and typography characteristics.
|
||||
|
||||
### Text (Density Level)
|
||||
- `none`: No text elements, full visual area
|
||||
- `title-only`: Single headline (≤8 characters), 85% visual area
|
||||
- `title-subtitle`: Title + context (≤15 chars), 75% visual area
|
||||
- `text-rich`: Title + subtitle + 2-4 keyword tags, 60% visual area
|
||||
|
||||
### Mood (Emotional Intensity)
|
||||
- `subtle`: Low contrast, muted/desaturated colors, light visual weight, calm aesthetic
|
||||
- `balanced`: Medium contrast, normal saturation, balanced visual weight
|
||||
- `bold`: High contrast, vivid/saturated colors, heavy visual weight, dynamic energy
|
||||
|
||||
## Text Style (When Title Included)
|
||||
|
||||
- **ALL text MUST be hand-drawn style**
|
||||
- Title text: Large, eye-catching, max 8 characters
|
||||
- May include 1 line of subtitle or keyword tags
|
||||
- Subtitle: Secondary, max 15 characters (if title-subtitle or text-rich)
|
||||
- Tags: 2-4 keyword badges (if text-rich)
|
||||
- Font style harmonizes with illustration style
|
||||
- **DO NOT use realistic or computer-generated fonts**
|
||||
|
||||
## Mood Application
|
||||
|
||||
Apply mood adjustments to the base style:
|
||||
|
||||
| Mood | Contrast | Saturation | Weight |
|
||||
|------|----------|------------|--------|
|
||||
| subtle | Reduce 20-30% | Desaturate 20-30% | Lighter strokes/fills |
|
||||
| balanced | Standard | Standard | Standard |
|
||||
| bold | Increase 20-30% | Increase 20-30% | Heavier strokes/fills |
|
||||
|
||||
## Language
|
||||
|
||||
- Use the same language as the content provided below for any text elements
|
||||
|
||||
@@ -93,7 +93,19 @@ options:
|
||||
description: "Square, social media friendly"
|
||||
```
|
||||
|
||||
### Question 5: Save Location
|
||||
### Question 5: Quick Mode
|
||||
|
||||
```
|
||||
header: "Quick"
|
||||
question: "Enable quick mode by default?"
|
||||
options:
|
||||
- label: "No (Recommended)"
|
||||
description: "Confirm dimension choices each time"
|
||||
- label: "Yes"
|
||||
description: "Skip confirmation, use auto-selection"
|
||||
```
|
||||
|
||||
### Question 6: Save Location
|
||||
|
||||
```
|
||||
header: "Save"
|
||||
@@ -123,7 +135,7 @@ options:
|
||||
|
||||
```yaml
|
||||
---
|
||||
version: 1
|
||||
version: 2
|
||||
watermark:
|
||||
enabled: [true/false]
|
||||
content: "[user input or empty]"
|
||||
@@ -131,12 +143,25 @@ watermark:
|
||||
opacity: 0.7
|
||||
preferred_type: [selected type or null]
|
||||
preferred_style: [selected style or null]
|
||||
preferred_text: title-only
|
||||
preferred_mood: balanced
|
||||
default_aspect: [2.35:1/16:9/1:1]
|
||||
quick_mode: [true/false]
|
||||
language: null
|
||||
custom_styles: []
|
||||
---
|
||||
```
|
||||
|
||||
## New Fields in v2
|
||||
|
||||
| Field | Default | Description |
|
||||
|-------|---------|-------------|
|
||||
| `preferred_text` | title-only | Text density (none, title-only, title-subtitle, text-rich) |
|
||||
| `preferred_mood` | balanced | Mood intensity (subtle, balanced, bold) |
|
||||
| `quick_mode` | false | Skip confirmation step when true |
|
||||
|
||||
Note: Text and Mood preferences use sensible defaults (title-only, balanced) and don't require setup questions. Users can modify these in EXTEND.md directly.
|
||||
|
||||
## Modifying Preferences Later
|
||||
|
||||
Users can edit EXTEND.md directly or run setup again:
|
||||
|
||||
@@ -9,7 +9,7 @@ description: EXTEND.md YAML schema for baoyu-cover-image user preferences
|
||||
|
||||
```yaml
|
||||
---
|
||||
version: 1
|
||||
version: 2
|
||||
|
||||
watermark:
|
||||
enabled: false
|
||||
@@ -21,8 +21,14 @@ preferred_type: null # hero|conceptual|typography|metaphor|scene|minimal or
|
||||
|
||||
preferred_style: null # Built-in style name or null for auto-select
|
||||
|
||||
preferred_text: title-only # none|title-only|title-subtitle|text-rich
|
||||
|
||||
preferred_mood: balanced # subtle|balanced|bold
|
||||
|
||||
default_aspect: "2.35:1" # 2.35:1|16:9|1:1
|
||||
|
||||
quick_mode: false # Skip confirmation when true
|
||||
|
||||
language: null # zh|en|ja|ko|auto (null = auto-detect)
|
||||
|
||||
custom_styles:
|
||||
@@ -42,14 +48,17 @@ custom_styles:
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `version` | int | 1 | Schema version |
|
||||
| `version` | int | 2 | Schema version |
|
||||
| `watermark.enabled` | bool | false | Enable watermark |
|
||||
| `watermark.content` | string | "" | Watermark text (@username or custom) |
|
||||
| `watermark.position` | enum | bottom-right | Position on image |
|
||||
| `watermark.opacity` | float | 0.7 | Transparency (0.1-1.0) |
|
||||
| `preferred_type` | string | null | Type name or null for auto |
|
||||
| `preferred_style` | string | null | Style name or null for auto |
|
||||
| `preferred_text` | string | title-only | Text density level |
|
||||
| `preferred_mood` | string | balanced | Mood intensity level |
|
||||
| `default_aspect` | string | "2.35:1" | Default aspect ratio |
|
||||
| `quick_mode` | bool | false | Skip confirmation step |
|
||||
| `language` | string | null | Output language (null = auto-detect) |
|
||||
| `custom_styles` | array | [] | User-defined styles |
|
||||
|
||||
@@ -64,6 +73,23 @@ custom_styles:
|
||||
| `scene` | Atmospheric scene, narrative feel |
|
||||
| `minimal` | Minimalist composition, generous whitespace |
|
||||
|
||||
## Text Options
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `none` | Pure visual, no text elements |
|
||||
| `title-only` | Single headline (≤8 characters) |
|
||||
| `title-subtitle` | Title + subtitle (≤15 characters) |
|
||||
| `text-rich` | Title + subtitle + keyword tags (2-4) |
|
||||
|
||||
## Mood Options
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `subtle` | Low contrast, muted colors, calm aesthetic |
|
||||
| `balanced` | Medium contrast, normal saturation, versatile |
|
||||
| `bold` | High contrast, vivid colors, dynamic energy |
|
||||
|
||||
## Position Options
|
||||
|
||||
| Value | Description |
|
||||
@@ -98,12 +124,15 @@ custom_styles:
|
||||
|
||||
```yaml
|
||||
---
|
||||
version: 1
|
||||
version: 2
|
||||
watermark:
|
||||
enabled: true
|
||||
content: "@myhandle"
|
||||
preferred_type: null
|
||||
preferred_style: elegant
|
||||
preferred_text: title-only
|
||||
preferred_mood: balanced
|
||||
quick_mode: false
|
||||
---
|
||||
```
|
||||
|
||||
@@ -111,7 +140,7 @@ preferred_style: elegant
|
||||
|
||||
```yaml
|
||||
---
|
||||
version: 1
|
||||
version: 2
|
||||
watermark:
|
||||
enabled: true
|
||||
content: "myblog.com"
|
||||
@@ -122,8 +151,14 @@ preferred_type: conceptual
|
||||
|
||||
preferred_style: blueprint
|
||||
|
||||
preferred_text: title-subtitle
|
||||
|
||||
preferred_mood: subtle
|
||||
|
||||
default_aspect: "16:9"
|
||||
|
||||
quick_mode: true
|
||||
|
||||
language: en
|
||||
|
||||
custom_styles:
|
||||
@@ -138,3 +173,16 @@ custom_styles:
|
||||
best_for: "SaaS, enterprise, technical"
|
||||
---
|
||||
```
|
||||
|
||||
## Migration from v1
|
||||
|
||||
When loading v1 schema, auto-upgrade:
|
||||
|
||||
| v1 Field | v2 Field | Default Value |
|
||||
|----------|----------|---------------|
|
||||
| (missing) | `version` | 2 |
|
||||
| (missing) | `preferred_text` | title-only |
|
||||
| (missing) | `preferred_mood` | balanced |
|
||||
| (missing) | `quick_mode` | false |
|
||||
|
||||
v1 `--no-title` flag maps to `preferred_text: none`.
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
name: mood-dimension
|
||||
description: Emotional intensity dimension for cover images
|
||||
---
|
||||
|
||||
# Mood Dimension
|
||||
|
||||
Controls emotional intensity and visual weight of cover images.
|
||||
|
||||
## Values
|
||||
|
||||
| Value | Contrast | Saturation | Weight | Energy |
|
||||
|-------|:--------:|:----------:|:------:|:------:|
|
||||
| `subtle` | Low | Muted | Light | Calm |
|
||||
| `balanced` | Medium | Normal | Medium | Moderate |
|
||||
| `bold` | High | Vivid | Heavy | Dynamic |
|
||||
|
||||
## Detail
|
||||
|
||||
### subtle
|
||||
|
||||
Calm, understated visual presence.
|
||||
|
||||
**Characteristics**:
|
||||
- Low contrast between elements
|
||||
- Muted, desaturated colors
|
||||
- Light visual weight
|
||||
- Gentle, refined aesthetic
|
||||
- Soft edges and transitions
|
||||
|
||||
**Use Cases**:
|
||||
- Thought leadership content
|
||||
- Professional/corporate communications
|
||||
- Meditation, wellness topics
|
||||
- Academic or scholarly articles
|
||||
- Luxury brand aesthetics
|
||||
|
||||
**Color Guidance**:
|
||||
- Pastels, earth tones, neutrals
|
||||
- Low saturation (30-50%)
|
||||
- Soft gradients
|
||||
- Minimal color variety (2-3 colors)
|
||||
|
||||
### balanced
|
||||
|
||||
Versatile, harmonious visual presence.
|
||||
|
||||
**Characteristics**:
|
||||
- Medium contrast
|
||||
- Natural saturation levels
|
||||
- Balanced visual weight
|
||||
- Clear but not aggressive
|
||||
- Standard aesthetic approach
|
||||
|
||||
**Use Cases**:
|
||||
- General articles (default)
|
||||
- Most blog content
|
||||
- Educational material
|
||||
- Product documentation
|
||||
- News and updates
|
||||
|
||||
**Color Guidance**:
|
||||
- Standard saturation (50-70%)
|
||||
- Complementary color schemes
|
||||
- Clear foreground/background separation
|
||||
- Moderate color variety (3-4 colors)
|
||||
|
||||
### bold
|
||||
|
||||
Dynamic, high-impact visual presence.
|
||||
|
||||
**Characteristics**:
|
||||
- High contrast between elements
|
||||
- Vivid, saturated colors
|
||||
- Heavy visual weight
|
||||
- Energetic, attention-grabbing
|
||||
- Sharp edges and strong shapes
|
||||
|
||||
**Use Cases**:
|
||||
- Product launches
|
||||
- Promotional announcements
|
||||
- Event marketing
|
||||
- Call-to-action content
|
||||
- Entertainment/gaming topics
|
||||
|
||||
**Color Guidance**:
|
||||
- High saturation (70-100%)
|
||||
- Vibrant, primary colors
|
||||
- Strong contrast ratios
|
||||
- Dynamic color combinations (4+ colors)
|
||||
|
||||
## Type Compatibility
|
||||
|
||||
| Type | subtle | balanced | bold |
|
||||
|------|:------:|:--------:|:----:|
|
||||
| hero | ✓ | ✓✓ | ✓✓ |
|
||||
| conceptual | ✓✓ | ✓✓ | ✓ |
|
||||
| typography | ✓ | ✓✓ | ✓✓ |
|
||||
| metaphor | ✓✓ | ✓✓ | ✓ |
|
||||
| scene | ✓✓ | ✓✓ | ✓ |
|
||||
| minimal | ✓✓ | ✓✓ | ✗ |
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
|
||||
## Style Interaction
|
||||
|
||||
Mood modifies the base style characteristics:
|
||||
|
||||
| Style Category | subtle | balanced | bold |
|
||||
|----------------|--------|----------|------|
|
||||
| Technical (blueprint, notion) | Lighter lines, softer colors | Standard rendering | Stronger contrast, sharper edges |
|
||||
| Artistic (watercolor, sketch-notes) | More whitespace, lighter strokes | Standard rendering | Deeper colors, heavier strokes |
|
||||
| Editorial (bold-editorial, dark-atmospheric) | Reduced contrast, softer tones | Standard rendering | Maximum impact, vivid colors |
|
||||
|
||||
## Auto Selection
|
||||
|
||||
When `--mood` is omitted, select based on signals:
|
||||
|
||||
| Signals | Mood Level |
|
||||
|---------|------------|
|
||||
| Professional, corporate, thought leadership, academic, luxury | `subtle` |
|
||||
| General, educational, standard, blog, documentation | `balanced` |
|
||||
| Launch, announcement, promotion, event, gaming, entertainment | `bold` |
|
||||
|
||||
Default: `balanced`
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
name: text-dimension
|
||||
description: Text density dimension for cover images
|
||||
---
|
||||
|
||||
# Text Dimension
|
||||
|
||||
Controls text density and information hierarchy on cover images.
|
||||
|
||||
## Values
|
||||
|
||||
| Value | Title | Subtitle | Tags | Visual Area |
|
||||
|-------|:-----:|:--------:|:----:|:-----------:|
|
||||
| `none` | - | - | - | 100% |
|
||||
| `title-only` | ✓ (≤8字) | - | - | 85% |
|
||||
| `title-subtitle` | ✓ | ✓ (≤15字) | - | 75% |
|
||||
| `text-rich` | ✓ | ✓ | ✓ (2-4) | 60% |
|
||||
|
||||
## Detail
|
||||
|
||||
### none
|
||||
|
||||
Pure visual cover with no text elements.
|
||||
|
||||
**Use Cases**:
|
||||
- Photography-focused covers
|
||||
- Abstract art pieces
|
||||
- Visual-only social sharing
|
||||
- When title added externally
|
||||
|
||||
**Composition**:
|
||||
- Full visual area available
|
||||
- No reserved text zones
|
||||
- Emphasis on visual metaphor
|
||||
|
||||
### title-only
|
||||
|
||||
Single headline, maximum impact.
|
||||
|
||||
**Use Cases**:
|
||||
- Most article covers (default)
|
||||
- Clear single message
|
||||
- Strong brand recognition
|
||||
|
||||
**Composition**:
|
||||
- Title: ≤8 characters, prominent
|
||||
- Reserved zone: top or bottom 15%
|
||||
- Visual supports title message
|
||||
|
||||
**Title Guidelines**:
|
||||
- Punchy, action-oriented
|
||||
- Numbers, questions, contrasts work well
|
||||
- Match content language
|
||||
|
||||
### title-subtitle
|
||||
|
||||
Title with supporting context.
|
||||
|
||||
**Use Cases**:
|
||||
- Technical articles needing clarification
|
||||
- Series with episode/part info
|
||||
- Content with dual messages
|
||||
|
||||
**Composition**:
|
||||
- Title: ≤8 characters, primary
|
||||
- Subtitle: ≤15 characters, secondary
|
||||
- Reserved zone: 25%
|
||||
- Clear hierarchy between title/subtitle
|
||||
|
||||
**Subtitle Guidelines**:
|
||||
- Clarify or contextualize title
|
||||
- Can include series name, author, date
|
||||
- Smaller, less prominent than title
|
||||
|
||||
### text-rich
|
||||
|
||||
Information-dense cover with multiple text elements.
|
||||
|
||||
**Use Cases**:
|
||||
- Infographic-style covers
|
||||
- Event announcements with details
|
||||
- Promotional material with features
|
||||
- Content with multiple key points
|
||||
|
||||
**Composition**:
|
||||
- Title: primary focus
|
||||
- Subtitle: supporting info
|
||||
- Tags: 2-4 keyword labels
|
||||
- Reserved zone: 40%
|
||||
- Clear visual hierarchy
|
||||
|
||||
**Tag Guidelines**:
|
||||
- 2-4 tags maximum
|
||||
- Short keywords (1-2 words each)
|
||||
- Positioned as badges/labels
|
||||
- Can highlight: category, date, author, key features
|
||||
|
||||
## Type Compatibility
|
||||
|
||||
| Type | none | title-only | title-subtitle | text-rich |
|
||||
|------|:----:|:----------:|:--------------:|:---------:|
|
||||
| hero | ✓ | ✓✓ | ✓✓ | ✓ |
|
||||
| conceptual | ✓✓ | ✓✓ | ✓ | ✓ |
|
||||
| typography | ✗ | ✓ | ✓✓ | ✓✓ |
|
||||
| metaphor | ✓✓ | ✓ | ✓ | ✗ |
|
||||
| scene | ✓✓ | ✓ | ✓ | ✗ |
|
||||
| minimal | ✓✓ | ✓✓ | ✓ | ✗ |
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
|
||||
## Auto Selection
|
||||
|
||||
When `--text` is omitted, select based on signals:
|
||||
|
||||
| Signals | Text Level |
|
||||
|---------|------------|
|
||||
| Visual-only, photography, abstract, art | `none` |
|
||||
| Article, blog, standard cover | `title-only` |
|
||||
| Series, tutorial, technical with context | `title-subtitle` |
|
||||
| Announcement, features, multiple points, infographic | `text-rich` |
|
||||
|
||||
Default: `title-only`
|
||||
@@ -76,7 +76,8 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image out.png --provi
|
||||
| `--model <id>`, `-m` | Model ID |
|
||||
| `--ar <ratio>` | Aspect ratio (e.g., `16:9`, `1:1`, `4:3`) |
|
||||
| `--size <WxH>` | Size (e.g., `1024x1024`) |
|
||||
| `--quality normal\|2k` | Quality preset (default: normal) |
|
||||
| `--quality normal\|2k` | Quality preset (default: 2k) |
|
||||
| `--imageSize 1K\|2K\|4K` | Image size for Google (default: from quality) |
|
||||
| `--ref <files...>` | Reference images (Google multimodal only) |
|
||||
| `--n <count>` | Number of images |
|
||||
| `--json` | JSON output |
|
||||
@@ -102,17 +103,20 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image out.png --provi
|
||||
|
||||
## Quality Presets
|
||||
|
||||
| Preset | Resolution | Use Case |
|
||||
|--------|------------|----------|
|
||||
| `normal` | ~1024px | Covers, illustrations |
|
||||
| `2k` | ~2048px | Infographics, slides |
|
||||
| Preset | Google imageSize | OpenAI Size | Use Case |
|
||||
|--------|------------------|-------------|----------|
|
||||
| `normal` | 1K | 1024px | Quick previews |
|
||||
| `2k` (default) | 2K | 2048px | Covers, illustrations, infographics |
|
||||
|
||||
**Google imageSize**: Can be overridden with `--imageSize 1K|2K|4K`
|
||||
|
||||
## Aspect Ratios
|
||||
|
||||
Supported: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `2.35:1`
|
||||
|
||||
- Multimodal models: embedded in prompt
|
||||
- Image-only models: uses `aspectRatio` or `size` parameter
|
||||
- Google multimodal: uses `imageConfig.aspectRatio`
|
||||
- Google Imagen: uses `aspectRatio` parameter
|
||||
- OpenAI: maps to closest supported size
|
||||
|
||||
## Error Handling
|
||||
|
||||
|
||||
@@ -1,34 +1,8 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { homedir } from "node:os";
|
||||
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
|
||||
type Provider = "google" | "openai";
|
||||
type Quality = "normal" | "2k";
|
||||
|
||||
type CliArgs = {
|
||||
prompt: string | null;
|
||||
promptFiles: string[];
|
||||
imagePath: string | null;
|
||||
provider: Provider | null;
|
||||
model: string | null;
|
||||
aspectRatio: string | null;
|
||||
size: string | null;
|
||||
quality: Quality;
|
||||
referenceImages: string[];
|
||||
n: number;
|
||||
json: boolean;
|
||||
help: boolean;
|
||||
};
|
||||
|
||||
const GOOGLE_MULTIMODAL_MODELS = [
|
||||
"gemini-3-pro-image-preview",
|
||||
];
|
||||
|
||||
const GOOGLE_IMAGEN_MODELS = ["imagen-3.0-generate-002", "imagen-3.0-generate-001"];
|
||||
|
||||
const OPENAI_IMAGE_MODELS = ["gpt-image-1.5", "gpt-image-1", "dall-e-3", "dall-e-2"];
|
||||
import type { CliArgs, Provider } from "./types";
|
||||
|
||||
function printUsage(): void {
|
||||
console.log(`Usage:
|
||||
@@ -44,7 +18,8 @@ Options:
|
||||
-m, --model <id> Model ID
|
||||
--ar <ratio> Aspect ratio (e.g., 16:9, 1:1, 4:3)
|
||||
--size <WxH> Size (e.g., 1024x1024)
|
||||
--quality normal|2k Quality preset (default: normal)
|
||||
--quality normal|2k Quality preset (default: 2k)
|
||||
--imageSize 1K|2K|4K Image size for Google (default: from quality)
|
||||
--ref <files...> Reference images (Google multimodal only)
|
||||
--n <count> Number of images (default: 1)
|
||||
--json JSON output
|
||||
@@ -53,6 +28,7 @@ Options:
|
||||
Environment variables:
|
||||
OPENAI_API_KEY OpenAI API key
|
||||
GOOGLE_API_KEY Google API key
|
||||
GEMINI_API_KEY Gemini API key (alias for GOOGLE_API_KEY)
|
||||
OPENAI_IMAGE_MODEL Default OpenAI model (gpt-image-1.5)
|
||||
GOOGLE_IMAGE_MODEL Default Google model (gemini-3-pro-image-preview)
|
||||
OPENAI_BASE_URL Custom OpenAI endpoint
|
||||
@@ -70,7 +46,8 @@ function parseArgs(argv: string[]): CliArgs {
|
||||
model: null,
|
||||
aspectRatio: null,
|
||||
size: null,
|
||||
quality: "normal",
|
||||
quality: "2k",
|
||||
imageSize: null,
|
||||
referenceImages: [],
|
||||
n: 1,
|
||||
json: false,
|
||||
@@ -161,6 +138,13 @@ function parseArgs(argv: string[]): CliArgs {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a === "--imageSize") {
|
||||
const v = argv[++i]?.toUpperCase();
|
||||
if (v !== "1K" && v !== "2K" && v !== "4K") throw new Error(`Invalid imageSize: ${v}`);
|
||||
out.imageSize = v;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a === "--ref" || a === "--reference") {
|
||||
const { items, next } = takeMany(i);
|
||||
if (items.length === 0) throw new Error(`Missing files for ${a}`);
|
||||
@@ -257,7 +241,7 @@ function normalizeOutputImagePath(p: string): string {
|
||||
function detectProvider(args: CliArgs): Provider {
|
||||
if (args.provider) return args.provider;
|
||||
|
||||
const hasGoogle = !!process.env.GOOGLE_API_KEY;
|
||||
const hasGoogle = !!(process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY);
|
||||
const hasOpenai = !!process.env.OPENAI_API_KEY;
|
||||
|
||||
if (hasGoogle && !hasOpenai) return "google";
|
||||
@@ -265,235 +249,21 @@ function detectProvider(args: CliArgs): Provider {
|
||||
if (hasGoogle && hasOpenai) return "google";
|
||||
|
||||
throw new Error(
|
||||
"No API key found. Set GOOGLE_API_KEY or OPENAI_API_KEY.\n" +
|
||||
"No API key found. Set GOOGLE_API_KEY, GEMINI_API_KEY, or OPENAI_API_KEY.\n" +
|
||||
"Create ~/.baoyu-skills/.env or <cwd>/.baoyu-skills/.env with your keys."
|
||||
);
|
||||
}
|
||||
|
||||
function getDefaultModel(provider: Provider): string {
|
||||
type ProviderModule = {
|
||||
getDefaultModel: () => string;
|
||||
generateImage: (prompt: string, model: string, args: CliArgs) => Promise<Uint8Array>;
|
||||
};
|
||||
|
||||
async function loadProviderModule(provider: Provider): Promise<ProviderModule> {
|
||||
if (provider === "google") {
|
||||
return process.env.GOOGLE_IMAGE_MODEL || "gemini-3-pro-image-preview";
|
||||
return (await import("./providers/google")) as ProviderModule;
|
||||
}
|
||||
return process.env.OPENAI_IMAGE_MODEL || "gpt-image-1.5";
|
||||
}
|
||||
|
||||
function isGoogleMultimodal(model: string): boolean {
|
||||
return GOOGLE_MULTIMODAL_MODELS.some((m) => model.includes(m));
|
||||
}
|
||||
|
||||
function isGoogleImagen(model: string): boolean {
|
||||
return GOOGLE_IMAGEN_MODELS.some((m) => model.includes(m));
|
||||
}
|
||||
|
||||
function buildPromptWithAspect(prompt: string, ar: string | null, quality: Quality): string {
|
||||
let result = prompt;
|
||||
if (ar) {
|
||||
result += ` Aspect ratio: ${ar}.`;
|
||||
}
|
||||
if (quality === "2k") {
|
||||
result += " High resolution 2048px.";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseAspectRatio(ar: string): { width: number; height: number } | null {
|
||||
const match = ar.match(/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)$/);
|
||||
if (!match) return null;
|
||||
const w = parseFloat(match[1]!);
|
||||
const h = parseFloat(match[2]!);
|
||||
if (w <= 0 || h <= 0) return null;
|
||||
return { width: w, height: h };
|
||||
}
|
||||
|
||||
function getOpenAISize(ar: string | null, quality: Quality): string {
|
||||
const base = quality === "2k" ? 2048 : 1024;
|
||||
|
||||
if (!ar) return `${base}x${base}`;
|
||||
|
||||
const parsed = parseAspectRatio(ar);
|
||||
if (!parsed) return `${base}x${base}`;
|
||||
|
||||
const ratio = parsed.width / parsed.height;
|
||||
|
||||
if (Math.abs(ratio - 1) < 0.1) return `${base}x${base}`;
|
||||
if (ratio > 1.5) return quality === "2k" ? "2048x1024" : "1792x1024";
|
||||
if (ratio < 0.67) return quality === "2k" ? "1024x2048" : "1024x1792";
|
||||
return `${base}x${base}`;
|
||||
}
|
||||
|
||||
async function readImageAsBase64(p: string): Promise<{ data: string; mimeType: string }> {
|
||||
const buf = await readFile(p);
|
||||
const ext = path.extname(p).toLowerCase();
|
||||
let mimeType = "image/png";
|
||||
if (ext === ".jpg" || ext === ".jpeg") mimeType = "image/jpeg";
|
||||
else if (ext === ".gif") mimeType = "image/gif";
|
||||
else if (ext === ".webp") mimeType = "image/webp";
|
||||
return { data: buf.toString("base64"), mimeType };
|
||||
}
|
||||
|
||||
async function generateWithGoogleMultimodal(
|
||||
prompt: string,
|
||||
model: string,
|
||||
args: CliArgs
|
||||
): Promise<Uint8Array> {
|
||||
const { generateText } = await import("ai");
|
||||
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
|
||||
|
||||
const google = createGoogleGenerativeAI({
|
||||
apiKey: process.env.GOOGLE_API_KEY,
|
||||
baseURL: process.env.GOOGLE_BASE_URL,
|
||||
});
|
||||
|
||||
const fullPrompt = buildPromptWithAspect(prompt, args.aspectRatio, args.quality);
|
||||
|
||||
const messages: any[] = [];
|
||||
const content: any[] = [];
|
||||
|
||||
for (const refPath of args.referenceImages) {
|
||||
const { data, mimeType } = await readImageAsBase64(refPath);
|
||||
content.push({ type: "image", image: data, mimeType });
|
||||
}
|
||||
content.push({ type: "text", text: fullPrompt });
|
||||
|
||||
messages.push({ role: "user", content });
|
||||
|
||||
const result = await generateText({
|
||||
model: google(model, { useSearchGrounding: false }),
|
||||
messages,
|
||||
providerOptions: {
|
||||
google: {
|
||||
responseModalities: ["TEXT", "IMAGE"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const files = (result as any).files;
|
||||
if (!files || files.length === 0) {
|
||||
const expRes = (result as any).response?.body?.candidates?.[0]?.content?.parts;
|
||||
if (expRes) {
|
||||
for (const part of expRes) {
|
||||
if (part.inlineData?.data) {
|
||||
return Uint8Array.from(Buffer.from(part.inlineData.data, "base64"));
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error("No image in response");
|
||||
}
|
||||
|
||||
const img = files[0];
|
||||
if (img.uint8Array) return img.uint8Array;
|
||||
if (img.base64) return Uint8Array.from(Buffer.from(img.base64, "base64"));
|
||||
|
||||
throw new Error("Cannot extract image data");
|
||||
}
|
||||
|
||||
async function generateWithGoogleImagen(
|
||||
prompt: string,
|
||||
model: string,
|
||||
args: CliArgs
|
||||
): Promise<Uint8Array> {
|
||||
const { experimental_generateImage: generateImage } = await import("ai");
|
||||
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
|
||||
|
||||
const google = createGoogleGenerativeAI({
|
||||
apiKey: process.env.GOOGLE_API_KEY,
|
||||
baseURL: process.env.GOOGLE_BASE_URL,
|
||||
});
|
||||
|
||||
const fullPrompt = buildPromptWithAspect(prompt, args.aspectRatio, args.quality);
|
||||
|
||||
const result = await generateImage({
|
||||
model: google.image(model),
|
||||
prompt: fullPrompt,
|
||||
n: args.n,
|
||||
aspectRatio: args.aspectRatio || undefined,
|
||||
});
|
||||
|
||||
const img = result.images[0];
|
||||
if (!img) throw new Error("No image in response");
|
||||
|
||||
if (img.uint8Array) return img.uint8Array;
|
||||
if (img.base64) return Uint8Array.from(Buffer.from(img.base64, "base64"));
|
||||
|
||||
throw new Error("Cannot extract image data");
|
||||
}
|
||||
|
||||
async function generateWithOpenAI(
|
||||
prompt: string,
|
||||
model: string,
|
||||
args: CliArgs
|
||||
): Promise<Uint8Array> {
|
||||
const baseURL = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1";
|
||||
const apiKey = process.env.OPENAI_API_KEY;
|
||||
|
||||
if (!apiKey) throw new Error("OPENAI_API_KEY is required");
|
||||
|
||||
const size = args.size || getOpenAISize(args.aspectRatio, args.quality);
|
||||
|
||||
const body: Record<string, any> = {
|
||||
model,
|
||||
prompt,
|
||||
size,
|
||||
};
|
||||
|
||||
if (model.includes("dall-e-3")) {
|
||||
body.quality = args.quality === "2k" ? "hd" : "standard";
|
||||
}
|
||||
|
||||
const res = await fetch(`${baseURL}/images/generations`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.text();
|
||||
throw new Error(`OpenAI API error: ${err}`);
|
||||
}
|
||||
|
||||
const result = (await res.json()) as { data: Array<{ url?: string; b64_json?: string }> };
|
||||
const img = result.data[0];
|
||||
|
||||
if (img?.b64_json) {
|
||||
return Uint8Array.from(Buffer.from(img.b64_json, "base64"));
|
||||
}
|
||||
|
||||
if (img?.url) {
|
||||
const imgRes = await fetch(img.url);
|
||||
if (!imgRes.ok) throw new Error("Failed to download image");
|
||||
const buf = await imgRes.arrayBuffer();
|
||||
return new Uint8Array(buf);
|
||||
}
|
||||
|
||||
throw new Error("No image in response");
|
||||
}
|
||||
|
||||
async function generate(
|
||||
provider: Provider,
|
||||
model: string,
|
||||
prompt: string,
|
||||
args: CliArgs
|
||||
): Promise<Uint8Array> {
|
||||
if (provider === "google") {
|
||||
if (isGoogleMultimodal(model)) {
|
||||
return generateWithGoogleMultimodal(prompt, model, args);
|
||||
}
|
||||
if (isGoogleImagen(model)) {
|
||||
if (args.referenceImages.length > 0) {
|
||||
console.error("Warning: Reference images not supported with Imagen models, ignoring.");
|
||||
}
|
||||
return generateWithGoogleImagen(prompt, model, args);
|
||||
}
|
||||
return generateWithGoogleMultimodal(prompt, model, args);
|
||||
}
|
||||
|
||||
if (args.referenceImages.length > 0) {
|
||||
console.error("Warning: Reference images not supported with OpenAI, ignoring.");
|
||||
}
|
||||
return generateWithOpenAI(prompt, model, args);
|
||||
return (await import("./providers/openai")) as ProviderModule;
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
@@ -525,7 +295,8 @@ async function main(): Promise<void> {
|
||||
}
|
||||
|
||||
const provider = detectProvider(args);
|
||||
const model = args.model || getDefaultModel(provider);
|
||||
const providerModule = await loadProviderModule(provider);
|
||||
const model = args.model || providerModule.getDefaultModel();
|
||||
const outputPath = normalizeOutputImagePath(args.imagePath);
|
||||
|
||||
let imageData: Uint8Array;
|
||||
@@ -533,7 +304,7 @@ async function main(): Promise<void> {
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
imageData = await generate(provider, model, prompt, args);
|
||||
imageData = await providerModule.generateImage(prompt, model, args);
|
||||
break;
|
||||
} catch (e) {
|
||||
if (!retried) {
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import path from "node:path";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import type { CliArgs } from "../types";
|
||||
|
||||
const GOOGLE_MULTIMODAL_MODELS = ["gemini-3-pro-image-preview"];
|
||||
const GOOGLE_IMAGEN_MODELS = ["imagen-3.0-generate-002", "imagen-3.0-generate-001"];
|
||||
|
||||
export function getDefaultModel(): string {
|
||||
return process.env.GOOGLE_IMAGE_MODEL || "gemini-3-pro-image-preview";
|
||||
}
|
||||
|
||||
function isGoogleMultimodal(model: string): boolean {
|
||||
return GOOGLE_MULTIMODAL_MODELS.some((m) => model.includes(m));
|
||||
}
|
||||
|
||||
function isGoogleImagen(model: string): boolean {
|
||||
return GOOGLE_IMAGEN_MODELS.some((m) => model.includes(m));
|
||||
}
|
||||
|
||||
function getGoogleApiKey(): string | null {
|
||||
return process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY || null;
|
||||
}
|
||||
|
||||
function getGoogleImageSize(args: CliArgs): "1K" | "2K" | "4K" {
|
||||
if (args.imageSize) return args.imageSize as "1K" | "2K" | "4K";
|
||||
return args.quality === "2k" ? "2K" : "1K";
|
||||
}
|
||||
|
||||
function buildPromptWithAspect(prompt: string, ar: string | null, quality: CliArgs["quality"]): string {
|
||||
let result = prompt;
|
||||
if (ar) {
|
||||
result += ` Aspect ratio: ${ar}.`;
|
||||
}
|
||||
if (quality === "2k") {
|
||||
result += " High resolution 2048px.";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function readImageAsBase64(p: string): Promise<{ data: string; mimeType: string }> {
|
||||
const buf = await readFile(p);
|
||||
const ext = path.extname(p).toLowerCase();
|
||||
let mimeType = "image/png";
|
||||
if (ext === ".jpg" || ext === ".jpeg") mimeType = "image/jpeg";
|
||||
else if (ext === ".gif") mimeType = "image/gif";
|
||||
else if (ext === ".webp") mimeType = "image/webp";
|
||||
return { data: buf.toString("base64"), mimeType };
|
||||
}
|
||||
|
||||
async function generateWithGemini(
|
||||
prompt: string,
|
||||
model: string,
|
||||
args: CliArgs
|
||||
): Promise<Uint8Array> {
|
||||
const { GoogleGenAI } = await import("@google/genai");
|
||||
|
||||
const apiKey = getGoogleApiKey();
|
||||
if (!apiKey) throw new Error("GOOGLE_API_KEY or GEMINI_API_KEY is required");
|
||||
|
||||
const ai = new GoogleGenAI({
|
||||
apiKey,
|
||||
httpOptions: {
|
||||
baseUrl: process.env.GOOGLE_BASE_URL || undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const input: Array<{ type: "text" | "image"; text?: string; data?: string; mime_type?: string }> = [];
|
||||
for (const refPath of args.referenceImages) {
|
||||
const { data, mimeType } = await readImageAsBase64(refPath);
|
||||
input.push({ type: "image", data, mime_type: mimeType });
|
||||
}
|
||||
input.push({ type: "text", text: prompt });
|
||||
|
||||
const imageConfig: { image_size: "1K" | "2K" | "4K"; aspect_ratio?: string } = {
|
||||
image_size: getGoogleImageSize(args),
|
||||
};
|
||||
if (args.aspectRatio) {
|
||||
imageConfig.aspect_ratio = args.aspectRatio;
|
||||
}
|
||||
|
||||
console.log("Generating image with Gemini...", imageConfig);
|
||||
const interaction = await ai.interactions.create({
|
||||
model,
|
||||
input,
|
||||
response_modalities: ["image"],
|
||||
generation_config: {
|
||||
image_config: imageConfig,
|
||||
},
|
||||
});
|
||||
console.log("Generation completed.");
|
||||
|
||||
for (const output of interaction.outputs || []) {
|
||||
if (output.type === "image" && output.data) {
|
||||
return Uint8Array.from(Buffer.from(output.data, "base64"));
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("No image in response");
|
||||
}
|
||||
|
||||
async function generateWithImagen(
|
||||
prompt: string,
|
||||
model: string,
|
||||
args: CliArgs
|
||||
): Promise<Uint8Array> {
|
||||
const { experimental_generateImage: generateImage } = await import("ai");
|
||||
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
|
||||
|
||||
const google = createGoogleGenerativeAI({
|
||||
apiKey: getGoogleApiKey() || undefined,
|
||||
baseURL: process.env.GOOGLE_BASE_URL,
|
||||
});
|
||||
|
||||
const fullPrompt = buildPromptWithAspect(prompt, args.aspectRatio, args.quality);
|
||||
|
||||
const result = await generateImage({
|
||||
model: google.image(model),
|
||||
prompt: fullPrompt,
|
||||
n: args.n,
|
||||
aspectRatio: args.aspectRatio || undefined,
|
||||
});
|
||||
|
||||
const img = result.images[0];
|
||||
if (!img) throw new Error("No image in response");
|
||||
|
||||
if (img.uint8Array) return img.uint8Array;
|
||||
if (img.base64) return Uint8Array.from(Buffer.from(img.base64, "base64"));
|
||||
|
||||
throw new Error("Cannot extract image data");
|
||||
}
|
||||
|
||||
export async function generateImage(
|
||||
prompt: string,
|
||||
model: string,
|
||||
args: CliArgs
|
||||
): Promise<Uint8Array> {
|
||||
if (isGoogleImagen(model)) {
|
||||
if (args.referenceImages.length > 0) {
|
||||
console.error("Warning: Reference images not supported with Imagen models, ignoring.");
|
||||
}
|
||||
return generateWithImagen(prompt, model, args);
|
||||
}
|
||||
|
||||
if (!isGoogleMultimodal(model) && args.referenceImages.length > 0) {
|
||||
console.error("Warning: Reference images are only supported with Gemini multimodal models.");
|
||||
}
|
||||
|
||||
return generateWithGemini(prompt, model, args);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import type { CliArgs } from "../types";
|
||||
|
||||
export function getDefaultModel(): string {
|
||||
return process.env.OPENAI_IMAGE_MODEL || "gpt-image-1.5";
|
||||
}
|
||||
|
||||
function parseAspectRatio(ar: string): { width: number; height: number } | null {
|
||||
const match = ar.match(/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)$/);
|
||||
if (!match) return null;
|
||||
const w = parseFloat(match[1]!);
|
||||
const h = parseFloat(match[2]!);
|
||||
if (w <= 0 || h <= 0) return null;
|
||||
return { width: w, height: h };
|
||||
}
|
||||
|
||||
type SizeMapping = {
|
||||
square: string;
|
||||
landscape: string;
|
||||
portrait: string;
|
||||
};
|
||||
|
||||
function getOpenAISize(
|
||||
model: string,
|
||||
ar: string | null,
|
||||
quality: CliArgs["quality"]
|
||||
): string {
|
||||
const isDalle3 = model.includes("dall-e-3");
|
||||
const isDalle2 = model.includes("dall-e-2");
|
||||
|
||||
if (isDalle2) {
|
||||
return "1024x1024";
|
||||
}
|
||||
|
||||
const sizes: SizeMapping = isDalle3
|
||||
? {
|
||||
square: "1024x1024",
|
||||
landscape: "1792x1024",
|
||||
portrait: "1024x1792",
|
||||
}
|
||||
: {
|
||||
square: "1024x1024",
|
||||
landscape: "1536x1024",
|
||||
portrait: "1024x1536",
|
||||
};
|
||||
|
||||
if (!ar) return sizes.square;
|
||||
|
||||
const parsed = parseAspectRatio(ar);
|
||||
if (!parsed) return sizes.square;
|
||||
|
||||
const ratio = parsed.width / parsed.height;
|
||||
|
||||
if (Math.abs(ratio - 1) < 0.1) return sizes.square;
|
||||
if (ratio > 1.5) return sizes.landscape;
|
||||
if (ratio < 0.67) return sizes.portrait;
|
||||
return sizes.square;
|
||||
}
|
||||
|
||||
export async function generateImage(
|
||||
prompt: string,
|
||||
model: string,
|
||||
args: CliArgs
|
||||
): Promise<Uint8Array> {
|
||||
const baseURL = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1";
|
||||
const apiKey = process.env.OPENAI_API_KEY;
|
||||
|
||||
if (!apiKey) throw new Error("OPENAI_API_KEY is required");
|
||||
|
||||
if (args.referenceImages.length > 0) {
|
||||
console.error("Warning: Reference images not supported with OpenAI, ignoring.");
|
||||
}
|
||||
|
||||
const size = args.size || getOpenAISize(model, args.aspectRatio, args.quality);
|
||||
|
||||
const body: Record<string, any> = {
|
||||
model,
|
||||
prompt,
|
||||
size,
|
||||
};
|
||||
|
||||
if (model.includes("dall-e-3")) {
|
||||
body.quality = args.quality === "2k" ? "hd" : "standard";
|
||||
}
|
||||
|
||||
const res = await fetch(`${baseURL}/images/generations`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.text();
|
||||
throw new Error(`OpenAI API error: ${err}`);
|
||||
}
|
||||
|
||||
const result = (await res.json()) as { data: Array<{ url?: string; b64_json?: string }> };
|
||||
const img = result.data[0];
|
||||
|
||||
if (img?.b64_json) {
|
||||
return Uint8Array.from(Buffer.from(img.b64_json, "base64"));
|
||||
}
|
||||
|
||||
if (img?.url) {
|
||||
const imgRes = await fetch(img.url);
|
||||
if (!imgRes.ok) throw new Error("Failed to download image");
|
||||
const buf = await imgRes.arrayBuffer();
|
||||
return new Uint8Array(buf);
|
||||
}
|
||||
|
||||
throw new Error("No image in response");
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
export type Provider = "google" | "openai";
|
||||
export type Quality = "normal" | "2k";
|
||||
|
||||
export type CliArgs = {
|
||||
prompt: string | null;
|
||||
promptFiles: string[];
|
||||
imagePath: string | null;
|
||||
provider: Provider | null;
|
||||
model: string | null;
|
||||
aspectRatio: string | null;
|
||||
size: string | null;
|
||||
quality: Quality;
|
||||
imageSize: string | null;
|
||||
referenceImages: string[];
|
||||
n: number;
|
||||
json: boolean;
|
||||
help: boolean;
|
||||
};
|
||||
Reference in New Issue
Block a user