Compare commits

...

37 Commits

Author SHA1 Message Date
Jim Liu 宝玉 013c72fce7 chore: release v1.24.3 2026-01-28 13:56:48 -06:00
Jim Liu 宝玉 40fadcb1f6 docs: emphasize updating prompt files before regenerating 2026-01-28 13:56:35 -06:00
Jim Liu 宝玉 499a1ee478 chore: release v1.24.2 2026-01-28 13:14:44 -06:00
Jim Liu 宝玉 fa89eaf2f7 refactor(baoyu-image-gen): default to sequential generation 2026-01-28 13:14:39 -06:00
Jim Liu 宝玉 7964e20bc3 chore: release v1.24.1 2026-01-28 12:29:43 -06:00
Jim Liu 宝玉 45c109ce88 Merge pull request #27 from JianJang2017/my-baoyu-skills
Adapting baoyu-image-gen to the Tongyi Text-to-Image Model
2026-01-28 12:23:34 -06:00
jianzhang50 74c08def06 docs: add Aliyun text-to-image model config to README.md 2026-01-28 22:04:53 +08:00
jianzhang50 907c8ab852 Adapting baoyu-image-gen to the Tongyi Text-to-Image Model 2026-01-28 09:04:57 +08:00
Jim Liu 宝玉 64945f3341 chore: release v1.24.0 2026-01-27 10:10:44 -06:00
Jim Liu 宝玉 050d37c344 docs: backfill third-party contributor attributions in changelogs 2026-01-27 10:06:22 -06:00
Jim Liu 宝玉 f9f168fc1f docs(release-skills): add third-party contributor attribution to release workflow 2026-01-27 10:06:15 -06:00
Jim Liu 宝玉 b1af3a3e45 fix(baoyu-post-to-wechat): improve title extraction, summary auto-fill, and content verification 2026-01-27 10:06:11 -06:00
Jim Liu 宝玉 0727296592 Merge pull request #23 from AliceLJY/feat/reuse-existing-chrome
feat(baoyu-post-to-wechat): reuse existing Chrome instead of requiring all windows closed
2026-01-27 08:03:46 -06:00
Jim Liu 宝玉 0e571b72fb chore: release v1.23.1 2026-01-27 00:59:34 -06:00
Jim Liu 宝玉 366630f8c3 fix(baoyu-compress-image): rename original as backup instead of deleting 2026-01-27 00:59:30 -06:00
安闲静雅 6bfafe0ec5 feat(baoyu-post-to-wechat): reuse existing Chrome instead of requiring all windows closed
Previously, the script always launched a new Chrome instance, which failed
when Chrome was already running due to profile lock conflicts on macOS.
Users had to close all Chrome windows before publishing.

This change adds auto-detection of existing Chrome debug ports and reuses
an already-logged-in WeChat tab when available, falling back to launching
a new instance only when no existing Chrome is found.

Changes:
- cdp.ts: add tryConnectExisting() and findExistingChromeDebugPort()
- wechat-article.ts: try existing Chrome before launching new one,
  reuse logged-in WeChat tab (identified by token= in URL),
  add waitForElement() for reliable menu detection,
  add --cdp-port option for manual override,
  fix process not exiting after completion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:16:33 +08:00
Jim Liu 宝玉 5e597f0a8b chore: release v1.23.0 2026-01-26 11:36:06 -06:00
Jim Liu 宝玉 40f4d2f730 refactor(baoyu-cover-image): replace 20 styles with 5D palette×rendering system
Migrate from 4-dimension (Type×Style×Text×Mood) to 5-dimension
(Type×Palette×Rendering×Text×Mood) system. 9 palettes × 6 renderings
= 54 combinations replacing 20 fixed styles. Add style presets for
backward compatibility, v2→v3 schema migration, and new reference
structure (palettes/, renderings/, workflow/).
2026-01-26 11:32:30 -06:00
Jim Liu 宝玉 b12ae7374d chore: release v1.22.0 2026-01-25 21:05:37 -06:00
Jim Liu 宝玉 1afa6f5a58 refactor(baoyu-post-to-x): simplify image placeholders from bracket format to XIMGPH 2026-01-25 21:05:08 -06:00
Jim Liu 宝玉 977598d5ae feat(baoyu-post-to-wechat): add theme selection, HTML preview, and simplify image placeholders 2026-01-25 21:05:04 -06:00
Jim Liu 宝玉 eb738aa61a feat(baoyu-cover-image): add default_output_dir preference with output directory selection 2026-01-25 21:04:59 -06:00
Jim Liu 宝玉 e9a030f917 feat(baoyu-article-illustrator): add imgs-subdir output option and improve style selection 2026-01-25 21:04:56 -06:00
Jim Liu 宝玉 e6a9b217e6 chore: release v1.21.4 2026-01-25 16:10:08 -06:00
Jim Liu 宝玉 ec1743592c fix(baoyu-post-to-wechat): fix regressions from Windows compatibility PR
- Fix broken md-to-wechat-fixed.ts/wechat-article-fixed.ts filename
  references that cause runtime crash (files were never renamed)
- Restore frontmatter quote stripping for title/summary values
- Restore --title CLI parameter functionality (was silently ignored)
- Fix summary extraction to properly skip headings, quotes, lists
- Fix argument parsing to reject single-dash args as file paths
- Remove debug console.error logs left from development
2026-01-25 16:09:24 -06:00
Jim Liu 宝玉 fe647a11bb Merge pull request #20 from JadeLiang003/fix/windows-copy-paste-support
Fix: Windows compatibility for baoyu-post-to-wechat
2026-01-25 16:04:01 -06:00
JadeLiang 5a11b49b00 Fix: Windows compatibility for baoyu-post-to-wechat
## Problem
The baoyu-post-to-wechat skill failed on Windows due to:
1. `new URL(import.meta.url).pathname` returns incorrect path format on Windows
2. Copy/paste operations used `xdotool` (Linux tool) which doesn't exist on Windows

## Solution
1. **md-to-wechat.ts**: Use `fileURLToPath()` to correctly resolve file paths on Windows
2. **wechat-article.ts**: Use CDP `Input.dispatchKeyEvent` for copy/paste operations
   - Replaces system-dependent tools (xdotool/osascript)
   - Works consistently across Windows/macOS/Linux
   - More reliable as it operates within Chrome session

## Changes
- Import `fileURLToPath` from 'node:url'
- Replace `new URL(import.meta.url).pathname` with `fileURLToPath(import.meta.url)`
- Replace system tool calls with CDP keyboard events for Ctrl+C and Ctrl+V
- Add platform-specific modifier handling (Cmd for macOS, Ctrl for Windows/Linux)

## Testing
- Tested successfully on Windows 11
- Markdown conversion works correctly
- HTML copy/paste to WeChat editor works correctly

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 15:24:04 +08:00
Jim Liu 宝玉 04692515bb fix: remove opacity of watermark 2026-01-24 17:23:04 -06:00
Jim Liu 宝玉 eb54c2a2e7 chore: release v1.21.3 2026-01-24 17:20:02 -06:00
Jim Liu 宝玉 a39c8eb0bd refactor(baoyu-article-illustrator): extract content to reference files
- Simplify SKILL.md by extracting usage and prompt templates
- Add references/usage.md for command syntax
- Add references/prompt-construction.md for prompt templates
- Add default_output_dir preference option
- Reorganize workflow from 5 to 6 steps with Pre-check phase
2026-01-24 17:19:42 -06:00
Jim Liu 宝玉 715555c2ab chore: release v1.21.2 2026-01-24 13:37:58 -06:00
Jim Liu 宝玉 02b039f2ff feat(baoyu-image-gen): add parallel generation documentation 2026-01-24 13:37:42 -06:00
Jim Liu 宝玉 00306414fe docs(release-skills): add skill grouping and user confirmation workflow 2026-01-24 13:37:38 -06:00
Jim Liu 宝玉 db1179e057 chore: release v1.21.1 2026-01-24 13:25:15 -06:00
Jim Liu 宝玉 7b0c9ca372 chore: release v1.21.0 2026-01-24 03:33:10 -06:00
Jim Liu 宝玉 e7255efdd6 chore: release v1.20.0 2026-01-24 02:30:28 -06:00
Jim Liu 宝玉 b15a95e73d chore: release v1.19.0 2026-01-24 01:24:41 -06:00
84 changed files with 4137 additions and 2163 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
},
"metadata": {
"description": "Skills shared by Baoyu for improving daily work efficiency",
"version": "1.18.3"
"version": "1.24.3"
},
"plugins": [
{
+159 -44
View File
@@ -114,11 +114,17 @@ Display version change: `1.2.3 → 1.3.0`
For each detected changelog file:
1. **Identify language** from filename suffix
2. **Generate content in that language**:
2. **Detect third-party contributors**:
- Check merge commits: `git log ${LAST_TAG}..HEAD --merges --pretty=format:"%H %s"`
- For each merged PR, identify the PR author via `gh pr view <number> --json author --jq '.author.login'`
- Compare against repo owner (`gh repo view --json owner --jq '.owner.login'`)
- If PR author ≠ repo owner → third-party contributor
3. **Generate content in that language**:
- Section titles in target language
- Change descriptions written naturally in target language (not translated)
- Date format: YYYY-MM-DD (universal)
3. **Insert at file head** (preserve existing content)
- **Third-party contributions**: Append contributor attribution `(by @username)` to the changelog entry
4. **Insert at file head** (preserve existing content)
**Section Title Translations** (built-in):
@@ -138,6 +144,7 @@ For each detected changelog file:
### Features
- Description of new feature
- Description of third-party contribution (by @username)
### Fixes
- Description of fix
@@ -148,6 +155,12 @@ For each detected changelog file:
Only include sections that have changes. Omit empty sections.
**Third-Party Attribution Rules**:
- Only add `(by @username)` for contributors who are NOT the repo owner
- Use GitHub username with `@` prefix
- Place at the end of the changelog entry line
- Apply to all languages consistently (always use `(by @username)` format, not translated)
**Multi-language Example**:
English (CHANGELOG.md):
@@ -155,7 +168,7 @@ English (CHANGELOG.md):
## 1.3.0 - 2026-01-22
### Features
- Add user authentication module
- Add user authentication module (by @contributor1)
- Support OAuth2 login
### Fixes
@@ -167,7 +180,7 @@ Chinese (CHANGELOG.zh.md):
## 1.3.0 - 2026-01-22
### 新功能
- 新增用户认证模块
- 新增用户认证模块 (by @contributor1)
- 支持 OAuth2 登录
### 修复
@@ -179,25 +192,68 @@ Japanese (CHANGELOG.ja.md):
## 1.3.0 - 2026-01-22
### 新機能
- ユーザー認証モジュールを追加
- ユーザー認証モジュールを追加 (by @contributor1)
- OAuth2 ログインをサポート
### 修正
- コネクションプールのメモリリークを修正
```
### Step 5: Check README Updates
### Step 5: Group Changes by Skill/Module
**IMPORTANT**: Before updating version file, check if README needs updates.
Analyze commits since last tag and group by affected skill/module:
1. **Scan for README files**: `README*.md` in project root
2. **For each skill/feature changed**, check if README documentation matches:
- Options/flags documented correctly
- Usage examples reflect current syntax
- Feature descriptions are accurate
3. **If README is outdated**:
- Update README.md (and README.zh.md if exists) to reflect changes
- Include in release commit
1. **Identify changed files** per commit
2. **Group by skill/module**:
- `skills/<skill-name>/*` → Group under that skill
- Root files (CLAUDE.md, etc.) → Group as "project"
- Multiple skills in one commit → Split into multiple groups
3. **For each group**, identify related README updates needed
**Example Grouping**:
```
baoyu-cover-image:
- feat: add new style options
- fix: handle transparent backgrounds
→ README updates: options table
baoyu-comic:
- refactor: improve panel layout algorithm
→ No README updates needed
project:
- docs: update CLAUDE.md architecture section
```
### Step 6: Commit Each Skill/Module Separately
For each skill/module group (in order of changes):
1. **Check README updates needed**:
- Scan `README*.md` for mentions of this skill/module
- Verify options/flags documented correctly
- Update usage examples if syntax changed
- Update feature descriptions if behavior changed
2. **Stage and commit**:
```bash
git add skills/<skill-name>/*
git add README.md README.zh.md # If updated for this skill
git commit -m "<type>(<skill-name>): <meaningful description>"
```
3. **Commit message format**:
- Use conventional commit format: `<type>(<scope>): <description>`
- `<type>`: feat, fix, refactor, docs, perf, etc.
- `<scope>`: skill name or "project"
- `<description>`: Clear, meaningful description of changes
**Example Commits**:
```bash
git commit -m "feat(baoyu-cover-image): add watercolor and minimalist styles"
git commit -m "fix(baoyu-comic): improve panel layout for long dialogues"
git commit -m "docs(project): update architecture documentation"
```
**Common README Updates Needed**:
| Change Type | README Section to Check |
@@ -208,11 +264,13 @@ Japanese (CHANGELOG.ja.md):
| Breaking changes | Migration notes, deprecation warnings |
| Restructured internals | Architecture section (if exposed to users) |
### Step 6: Update Version File
### Step 7: Generate Changelog and Update Version
1. Read version file (JSON/TOML/text)
2. Update version number
3. Write back (preserve formatting)
1. **Generate multi-language changelogs** (as described in Step 4)
2. **Update version file**:
- Read version file (JSON/TOML/text)
- Update version number
- Write back (preserve formatting)
**Version Paths by File Type**:
@@ -224,25 +282,77 @@ Japanese (CHANGELOG.ja.md):
| marketplace.json | `$.metadata.version` |
| VERSION / version.txt | Direct content |
### Step 7: Commit and Tag
### Step 8: User Confirmation
```bash
git add <all modified files>
git commit -m "chore: release v{VERSION}"
git tag v{VERSION}
Before creating the release commit, ask user to confirm:
**Use AskUserQuestion with two questions**:
1. **Version bump** (single select):
- Show recommended version based on Step 3 analysis
- Options: recommended (with label), other semver options
- Example: `1.2.3 → 1.3.0 (Recommended)`, `1.2.3 → 1.2.4`, `1.2.3 → 2.0.0`
2. **Push to remote** (single select):
- Options: "Yes, push after commit", "No, keep local only"
**Example Output Before Confirmation**:
```
Commits created:
1. feat(baoyu-cover-image): add watercolor and minimalist styles
2. fix(baoyu-comic): improve panel layout for long dialogues
3. docs(project): update architecture documentation
Changelog preview (en):
## 1.3.0 - 2026-01-22
### Features
- Add watercolor and minimalist styles to cover-image
### Fixes
- Improve panel layout for long dialogues in comic
Ready to create release commit and tag.
```
### Step 9: Create Release Commit and Tag
After user confirmation:
1. **Stage version and changelog files**:
```bash
git add <version-file>
git add CHANGELOG*.md
```
2. **Create release commit**:
```bash
git commit -m "chore: release v{VERSION}"
```
3. **Create tag**:
```bash
git tag v{VERSION}
```
4. **Push if user confirmed** (Step 8):
```bash
git push origin main
git push origin v{VERSION}
```
**Note**: Do NOT add Co-Authored-By line. This is a release commit, not a code contribution.
**Important**: Do NOT push to remote. User will push manually when ready.
**Post-Release Output**:
```
Release v1.3.0 created locally.
Release v1.3.0 created.
To publish:
git push origin main
git push origin v1.3.0
Commits:
1. feat(baoyu-cover-image): add watercolor and minimalist styles
2. fix(baoyu-comic): improve panel layout for long dialogues
3. docs(project): update architecture documentation
4. chore: release v1.3.0
Tag: v1.3.0
Status: Pushed to origin # or "Local only - run git push when ready"
```
## Configuration (.releaserc.yml)
@@ -307,31 +417,36 @@ Project detected:
Last tag: v1.2.3
Proposed version: v1.3.0
Changes detected:
feat: add user authentication
feat: support oauth2 login
fix: memory leak in pool
Changes grouped by skill/module:
baoyu-cover-image:
- feat: add watercolor style
- feat: add minimalist style
→ Commit: feat(baoyu-cover-image): add watercolor and minimalist styles
→ README updates: options table
baoyu-comic:
- fix: panel layout for long dialogues
→ Commit: fix(baoyu-comic): improve panel layout for long dialogues
→ No README updates
Changelog preview (en):
## 1.3.0 - 2026-01-22
### Features
- Add user authentication module
- Support OAuth2 login
- Add watercolor and minimalist styles to cover-image
### Fixes
- Fix memory leak in connection pool
- Improve panel layout for long dialogues in comic
Changelog preview (zh):
## 1.3.0 - 2026-01-22
### 新功能
- 新增用户认证模块
- 支持 OAuth2 登录
- 为 cover-image 添加水彩和极简风格
### 修复
- 修复连接池内存泄漏问题
- 改进 comic 长对话的面板布局
Files to modify:
- package.json
- CHANGELOG.md
- CHANGELOG.zh.md
Commits to create:
1. feat(baoyu-cover-image): add watercolor and minimalist styles
2. fix(baoyu-comic): improve panel layout for long dialogues
3. chore: release v1.3.0
No changes made. Run without --dry-run to execute.
```
+5
View File
@@ -152,3 +152,8 @@ slide-deck/
infographic/
illustrations/
comic/
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
+112 -3
View File
@@ -2,6 +2,112 @@
English | [中文](./CHANGELOG.zh.md)
## 1.24.3 - 2026-01-28
### Documentation
- Emphasize updating prompt files before regenerating images in modification workflows (article-illustrator, slide-deck, xhs-images, cover-image, comic).
## 1.24.2 - 2026-01-28
### Refactor
- `baoyu-image-gen`: default to sequential generation; parallel available on request.
## 1.24.1 - 2026-01-28
### Features
- `baoyu-image-gen`: add Aliyun Tongyi Wanxiang (DashScope) text-to-image model support (by @JianJang2017).
### Documentation
- Add Aliyun text-to-image model configuration to README.
## 1.24.0 - 2026-01-27
### Features
- `baoyu-post-to-wechat`: reuse existing Chrome browser instead of requiring all windows closed (by @AliceLJY).
### Fixes
- `baoyu-post-to-wechat`: improves title extraction to support h1/h2 headings; adds summary auto-fill and content verification after paste/type; supports flexible HTML meta tag attribute ordering.
### Documentation
- `release-skills`: adds third-party contributor attribution rules to changelog workflow.
- Backfills missing third-party contributor attributions across historical changelog entries.
## 1.23.1 - 2026-01-27
### Fixes
- `baoyu-compress-image`: rename original file as `_original` backup instead of deleting after compression.
## 1.23.0 - 2026-01-26
### Refactor
- `baoyu-cover-image`: replaces 20 fixed styles with 5-dimension system (Type × Palette × Rendering × Text × Mood). 9 color palettes × 6 rendering styles = 54 combinations. Adds style presets for backward compatibility, v2→v3 schema migration, and new reference structure (`palettes/`, `renderings/`, `workflow/`).
## 1.22.0 - 2026-01-25
### Features
- `baoyu-article-illustrator`: adds `imgs-subdir` output directory option; improves style selection to always ask and show preferred_style from EXTEND.md.
- `baoyu-cover-image`: adds `default_output_dir` preference supporting `same-dir`, `imgs-subdir`, and `independent` options with Step 1.5 for output directory selection.
- `baoyu-post-to-wechat`: adds theme selection (default/grace/simple) with AskUserQuestion before posting; adds HTML preview step; simplifies image placeholders to `WECHATIMGPH_N` format; refactors copy/paste to cross-platform helpers.
### Refactor
- `baoyu-post-to-x`: simplifies image placeholders from `[[IMAGE_PLACEHOLDER_N]]` to `XIMGPH_N` format.
## 1.21.4 - 2026-01-25
### Fixes
- `baoyu-post-to-wechat`: adds Windows compatibility—uses `fileURLToPath` for correct path resolution, replaces system-dependent copy/paste tools (osascript/xdotool) with CDP keyboard events for cross-platform support (by @JadeLiang003).
- `baoyu-post-to-wechat`: fixes regressions from Windows compatibility PR—corrects broken `-fixed` filename references, restores frontmatter quote stripping, restores `--title` CLI parameter, fixes summary extraction to skip headings/quotes/lists, fixes argument parsing for single-dash flags, removes debug logs.
- `baoyu-article-illustrator`, `baoyu-cover-image`, `baoyu-xhs-images`: removes opacity option from watermark configuration.
## 1.21.3 - 2026-01-24
### Refactor
- `baoyu-article-illustrator`: simplifies SKILL.md by extracting content to reference files—adds `references/usage.md` for command syntax, `references/prompt-construction.md` for prompt templates. Reorganizes workflow from 5 to 6 steps with new Pre-check phase. Adds `default_output_dir` preference option.
## 1.21.2 - 2026-01-24
### Features
- `baoyu-image-gen`: adds parallel generation documentation with recommended 4 concurrent subagents for batch operations.
### Documentation
- `release-skills`: adds skill/module grouping workflow and user confirmation step before release.
## 1.21.1 - 2026-01-24
### Documentation
- `baoyu-comic`: adds character sheet compression step after generation to reduce token usage when used as reference image.
## 1.21.0 - 2026-01-24
### Features
- `baoyu-cover-image`: expands aspect ratio options—adds 4:3, 3:2, 3:4 ratios; changes default from 2.35:1 to 16:9 for better versatility. Aspect ratio is now always confirmed unless explicitly specified via `--aspect` flag.
- `baoyu-image-gen`: refactors Google provider to support both Gemini multimodal and Imagen models with unified API. Adds `--imageSize` parameter support (1K/2K/4K) for Gemini models.
## 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
@@ -99,7 +205,7 @@ English | [中文](./CHANGELOG.zh.md)
## 1.14.0 - 2026-01-22
### Fixes
- `baoyu-post-to-x`: improves video ready detection for more reliable video posting.
- `baoyu-post-to-x`: improves video ready detection for more reliable video posting (by @fkysly).
### Documentation
- `baoyu-slide-deck`: comprehensive SKILL.md enhancement—adds slide count guidance (recommended 8-25, max 30), audience guidelines table with audience-specific principles, style selection principles with content-type recommendations, layout selection tips with common mistakes to avoid, visual hierarchy principles, content density guidelines (McKinsey-style high-density principles), color selection guide, typography principles with font recommendations (English and Chinese fonts with multilingual pairing), and visual elements reference (backgrounds, typography treatments, geometric accents).
@@ -114,6 +220,9 @@ English | [中文](./CHANGELOG.zh.md)
## 1.12.0 - 2026-01-21
### Features
- `baoyu-post-to-x`: adds quote tweet support (by @threehotpot-bot).
### Refactor
- `baoyu-post-to-x`: extracts shared utilities to `x-utils.ts`—consolidates Chrome detection, CDP connection, clipboard operations, and helper functions from `x-article.ts`, `x-browser.ts`, `x-quote.ts`, and `x-video.ts` into a single reusable module.
@@ -129,7 +238,7 @@ English | [中文](./CHANGELOG.zh.md)
## 1.10.0 - 2026-01-21
### Features
- `baoyu-post-to-x`: adds video posting support—new `x-video.ts` script for posting text with video files (MP4, MOV, WebM). Supports preview mode and handles video processing timeouts.
- `baoyu-post-to-x`: adds video posting support—new `x-video.ts` script for posting text with video files (MP4, MOV, WebM). Supports preview mode and handles video processing timeouts (by @fkysly).
## 1.9.0 - 2026-01-20
@@ -185,7 +294,7 @@ English | [中文](./CHANGELOG.zh.md)
## 1.4.1 - 2026-01-18
### Fixes
- `baoyu-post-to-x`: supports multi-language UI selectors for X Articles (contributed by [@ianchenx](https://github.com/ianchenx)).
- `baoyu-post-to-x`: supports multi-language UI selectors for X Articles (by @ianchenx).
## 1.4.0 - 2026-01-18
+112 -3
View File
@@ -2,6 +2,112 @@
[English](./CHANGELOG.md) | 中文
## 1.24.3 - 2026-01-28
### 文档
- 在修改工作流中强调先更新提示词文件再生成图片(article-illustrator、slide-deck、xhs-images、cover-image、comic)。
## 1.24.2 - 2026-01-28
### 重构
- `baoyu-image-gen`:默认改为顺序生成图片;并行生成需明确请求。
## 1.24.1 - 2026-01-28
### 新功能
- `baoyu-image-gen`:新增阿里云通义万象(DashScope)文生图模型支持 (by @JianJang2017)。
### 文档
- README 中新增阿里云文生图模型配置说明。
## 1.24.0 - 2026-01-27
### 新功能
- `baoyu-post-to-wechat`:复用已打开的 Chrome 浏览器,无需关闭所有窗口 (by @AliceLJY)。
### 修复
- `baoyu-post-to-wechat`:改进标题提取,支持 h1/h2 标题;新增摘要自动填充和粘贴/输入后内容验证;支持 HTML meta 标签属性顺序灵活匹配。
### 文档
- `release-skills`:在发布流程中新增第三方贡献者署名规则。
- 补全历史 changelog 中缺失的第三方贡献者署名。
## 1.23.1 - 2026-01-27
### 修复
- `baoyu-compress-image`:压缩后将原始文件重命名为 `_original` 备份,不再删除。
## 1.23.0 - 2026-01-26
### 重构
- `baoyu-cover-image`:将 20 种固定风格替换为五维系统(类型 × 配色 × 渲染 × 文字 × 氛围)。9 种配色方案 × 6 种渲染风格 = 54 种组合。新增风格预设实现向后兼容,v2→v3 配置迁移,以及新的引用文件结构(`palettes/``renderings/``workflow/`)。
## 1.22.0 - 2026-01-25
### 新功能
- `baoyu-article-illustrator`:新增 `imgs-subdir` 输出目录选项;改进风格选择,始终询问并展示 EXTEND.md 中的 preferred_style。
- `baoyu-cover-image`:新增 `default_output_dir` 偏好设置,支持 `same-dir``imgs-subdir``independent` 选项,新增 Step 1.5 输出目录选择流程。
- `baoyu-post-to-wechat`:发布前新增主题选择(default/grace/simple);新增 HTML 预览步骤;图片占位符简化为 `WECHATIMGPH_N` 格式;重构复制粘贴为跨平台辅助函数。
### 重构
- `baoyu-post-to-x`:图片占位符从 `[[IMAGE_PLACEHOLDER_N]]` 简化为 `XIMGPH_N` 格式。
## 1.21.4 - 2026-01-25
### 修复
- `baoyu-post-to-wechat`:新增 Windows 兼容性——使用 `fileURLToPath` 正确解析路径,将系统依赖的复制粘贴工具(osascript/xdotool)替换为 CDP 键盘事件,实现跨平台支持 (by @JadeLiang003)。
- `baoyu-post-to-wechat`:修复 Windows 兼容性 PR 引入的回退问题——修正错误的 `-fixed` 文件名引用、恢复 frontmatter 引号剥离、恢复 `--title` CLI 参数、修复摘要提取逻辑以正确跳过标题/引用/列表、修复单横线参数解析、移除调试日志。
- `baoyu-article-illustrator``baoyu-cover-image``baoyu-xhs-images`:移除水印配置中的透明度选项。
## 1.21.3 - 2026-01-24
### 重构
- `baoyu-article-illustrator`:简化 SKILL.md,提取内容至引用文件——新增 `references/usage.md` 用于命令语法,`references/prompt-construction.md` 用于提示词模板。工作流从 5 步重组为 6 步,新增 Pre-check 预检阶段。新增 `default_output_dir` 偏好设置选项。
## 1.21.2 - 2026-01-24
### 新功能
- `baoyu-image-gen`:添加并行生成文档,推荐使用 4 个并发 subagent 进行批量操作。
### 文档
- `release-skills`:新增按 skill/module 分组提交流程和发布前用户确认步骤。
## 1.21.1 - 2026-01-24
### 文档
- `baoyu-comic`:在角色参考图生成后添加压缩步骤,减少作为参考图使用时的 token 消耗。
## 1.21.0 - 2026-01-24
### 新功能
- `baoyu-cover-image`:扩展宽高比选项——新增 4:3、3:2、3:4 比例;默认值从 2.35:1 改为 16:9 以提高通用性。现在除非通过 `--aspect` 标志明确指定,否则始终确认宽高比。
- `baoyu-image-gen`:重构 Google provider 以统一支持 Gemini 多模态和 Imagen 模型。为 Gemini 模型新增 `--imageSize` 参数支持(1K/2K/4K)。
## 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
### 文档
@@ -99,7 +205,7 @@
## 1.14.0 - 2026-01-22
### 修复
- `baoyu-post-to-x`:改进视频就绪检测,提升视频发布稳定性。
- `baoyu-post-to-x`:改进视频就绪检测,提升视频发布稳定性 (by @fkysly)
### 文档
- `baoyu-slide-deck`:SKILL.md 全面增强——新增幻灯片数量指南(推荐 8-25 张,最多 30 张)、受众指南表格及各受众特定原则、风格选择原则与内容类型推荐、布局选择技巧与常见错误提示、视觉层次原则、内容密度指南(麦肯锡风格高密度原则)、配色选择指南、字体排版原则与字体推荐(中英文字体及多语言搭配方案)、视觉元素参考(背景处理、字体处理、几何装饰)。
@@ -114,6 +220,9 @@
## 1.12.0 - 2026-01-21
### 新功能
- `baoyu-post-to-x`:新增引用推文(Quote Tweet)支持 (by @threehotpot-bot)。
### 重构
- `baoyu-post-to-x`:提取公共工具函数到 `x-utils.ts`——将 `x-article.ts``x-browser.ts``x-quote.ts``x-video.ts` 中重复的 Chrome 检测、CDP 连接、剪贴板操作等功能整合为统一的可复用模块。
@@ -129,7 +238,7 @@
## 1.10.0 - 2026-01-21
### 新功能
- `baoyu-post-to-x`:新增视频发布支持——新增 `x-video.ts` 脚本,支持发布带视频的推文(MP4、MOV、WebM 格式)。支持预览模式,自动处理视频上传等待。
- `baoyu-post-to-x`:新增视频发布支持——新增 `x-video.ts` 脚本,支持发布带视频的推文(MP4、MOV、WebM 格式)。支持预览模式,自动处理视频上传等待 (by @fkysly)
## 1.9.0 - 2026-01-20
@@ -185,7 +294,7 @@
## 1.4.1 - 2026-01-18
### 修复
- `baoyu-post-to-x`:支持 X Articles 多语言 UI 选择器(感谢 [@ianchenx](https://github.com/ianchenx) 贡献)
- `baoyu-post-to-x`:支持 X Articles 多语言 UI 选择器 (by @ianchenx)
## 1.4.0 - 2026-01-18
+41 -39
View File
@@ -245,45 +245,35 @@ 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 5 dimensions: Type × Palette × Rendering × Text × Mood. Combines 9 color palettes with 6 rendering styles for 54 unique combinations.
```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
/baoyu-cover-image path/to/article.md --type conceptual --style blueprint
/baoyu-cover-image path/to/article.md --style warm
# Quick mode: skip confirmation, use auto-selection
/baoyu-cover-image path/to/article.md --quick
# Specify aspect ratio (default: 2.35:1)
/baoyu-cover-image path/to/article.md --aspect 16:9
# Specify dimensions (5D system)
/baoyu-cover-image path/to/article.md --type conceptual --palette cool --rendering digital
/baoyu-cover-image path/to/article.md --text title-subtitle --mood bold
# Without title text
# Style presets (backward-compatible shorthand)
/baoyu-cover-image path/to/article.md --style blueprint
# Specify aspect ratio (default: 16:9)
/baoyu-cover-image path/to/article.md --aspect 2.35:1
# Visual only (no title text)
/baoyu-cover-image path/to/article.md --no-title
```
Available types: `hero`, `conceptual`, `typography`, `metaphor`, `scene`, `minimal`
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`
**Style Previews**:
| | | |
|:---:|:---:|:---:|
| ![elegant](./screenshots/cover-image-styles/elegant.webp) | ![blueprint](./screenshots/cover-image-styles/blueprint.webp) | ![bold-editorial](./screenshots/cover-image-styles/bold-editorial.webp) |
| elegant | blueprint | bold-editorial |
| ![chalkboard](./screenshots/cover-image-styles/chalkboard.webp) | ![dark-atmospheric](./screenshots/cover-image-styles/dark-atmospheric.webp) | ![editorial-infographic](./screenshots/cover-image-styles/editorial-infographic.webp) |
| chalkboard | dark-atmospheric | editorial-infographic |
| ![fantasy-animation](./screenshots/cover-image-styles/fantasy-animation.webp) | ![intuition-machine](./screenshots/cover-image-styles/intuition-machine.webp) | ![minimal](./screenshots/cover-image-styles/minimal.webp) |
| fantasy-animation | intuition-machine | minimal |
| ![nature](./screenshots/cover-image-styles/nature.webp) | ![notion](./screenshots/cover-image-styles/notion.webp) | ![pixel-art](./screenshots/cover-image-styles/pixel-art.webp) |
| nature | notion | pixel-art |
| ![playful](./screenshots/cover-image-styles/playful.webp) | ![retro](./screenshots/cover-image-styles/retro.webp) | ![sketch-notes](./screenshots/cover-image-styles/sketch-notes.webp) |
| playful | retro | sketch-notes |
| ![vector-illustration](./screenshots/cover-image-styles/vector-illustration.webp) | ![vintage](./screenshots/cover-image-styles/vintage.webp) | ![warm](./screenshots/cover-image-styles/warm.webp) |
| vector-illustration | vintage | warm |
| ![watercolor](./screenshots/cover-image-styles/watercolor.webp) | ![flat-doodle](./screenshots/cover-image-styles/flat-doodle.webp) | |
| watercolor | flat-doodle | |
**Five Dimensions**:
- **Type**: `hero`, `conceptual`, `typography`, `metaphor`, `scene`, `minimal`
- **Palette**: `warm`, `elegant`, `cool`, `dark`, `earth`, `vivid`, `pastel`, `mono`, `retro`
- **Rendering**: `flat-vector`, `hand-drawn`, `painterly`, `digital`, `pixel`, `chalk`
- **Text**: `none`, `title-only` (default), `title-subtitle`, `text-rich`
- **Mood**: `subtle`, `balanced` (default), `bold`
#### baoyu-slide-deck
@@ -553,7 +543,7 @@ AI-powered generation backends.
#### baoyu-image-gen
AI SDK-based image generation using official OpenAI and Google APIs. Supports text-to-image, reference images, aspect ratios, and quality presets.
AI SDK-based image generation using official OpenAI, Google and DashScope (Aliyun Tongyi Wanxiang) APIs. Supports text-to-image, reference images, aspect ratios, and quality presets.
```bash
# Basic generation (auto-detect provider)
@@ -568,6 +558,9 @@ AI SDK-based image generation using official OpenAI and Google APIs. Supports te
# Specific provider
/baoyu-image-gen --prompt "A cat" --image cat.png --provider openai
# DashScope (Aliyun Tongyi Wanxiang)
/baoyu-image-gen --prompt "一只可爱的猫" --image cat.png --provider dashscope
# With reference images (Google multimodal only)
/baoyu-image-gen --prompt "Make it blue" --image out.png --ref source.png
```
@@ -578,7 +571,7 @@ AI SDK-based image generation using official OpenAI and Google APIs. Supports te
| `--prompt`, `-p` | Prompt text |
| `--promptfiles` | Read prompt from files (concatenated) |
| `--image` | Output image path (required) |
| `--provider` | `google` or `openai` (default: google) |
| `--provider` | `google`, `openai` or `dashscope` (default: google) |
| `--model`, `-m` | Model ID |
| `--ar` | Aspect ratio (e.g., `16:9`, `1:1`, `4:3`) |
| `--size` | Size (e.g., `1024x1024`) |
@@ -590,15 +583,18 @@ AI SDK-based image generation using official OpenAI and Google APIs. Supports te
|----------|-------------|---------|
| `OPENAI_API_KEY` | OpenAI API key | - |
| `GOOGLE_API_KEY` | Google API key | - |
| `DASHSCOPE_API_KEY` | DashScope API key (Aliyun) | - |
| `OPENAI_IMAGE_MODEL` | OpenAI model | `gpt-image-1.5` |
| `GOOGLE_IMAGE_MODEL` | Google model | `gemini-3-pro-image-preview` |
| `DASHSCOPE_IMAGE_MODEL` | DashScope model | `z-image-turbo` |
| `OPENAI_BASE_URL` | Custom OpenAI endpoint | - |
| `GOOGLE_BASE_URL` | Custom Google endpoint | - |
| `DASHSCOPE_BASE_URL` | Custom DashScope endpoint | - |
**Provider Auto-Selection**:
1. If `--provider` specified → use it
2. If only one API key available → use that provider
3. If both available → default to Google
3. If multiple available → default to Google
#### baoyu-danger-gemini-web
@@ -709,6 +705,11 @@ OPENAI_IMAGE_MODEL=gpt-image-1.5
GOOGLE_API_KEY=xxx
GOOGLE_IMAGE_MODEL=gemini-3-pro-image-preview
# GOOGLE_BASE_URL=https://generativelanguage.googleapis.com/v1beta
# DashScope (Aliyun Tongyi Wanxiang)
DASHSCOPE_API_KEY=sk-xxx
DASHSCOPE_IMAGE_MODEL=z-image-turbo
# DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/api/v1
EOF
```
@@ -737,13 +738,14 @@ mkdir -p .baoyu-skills/baoyu-cover-image
Then create `.baoyu-skills/baoyu-cover-image/EXTEND.md`:
```markdown
## Custom Styles
## Custom Palettes
### brand
- Primary color: #1a73e8
- Secondary color: #34a853
- Font style: Modern sans-serif
- Always include company logo watermark
### corporate-tech
- Primary colors: #1a73e8, #4A90D9
- Background: #F5F7FA
- Accent colors: #00B4D8, #48CAE4
- Decorative hints: Clean lines, subtle gradients
- Best for: SaaS, enterprise, technical
```
The extension content will be loaded before skill execution and override defaults.
+41 -39
View File
@@ -245,45 +245,35 @@ npx skills add jimliu/baoyu-skills
#### baoyu-cover-image
为文章生成封面图,支持类型 × 风格二维系统
为文章生成封面图,支持五维定制系统:类型 × 配色 × 渲染 × 文字 × 氛围。9 种配色方案与 6 种渲染风格组合,提供 54 种独特效果
```bash
# 根据内容自动选择类型和风格
# 根据内容自动选择所有维度
/baoyu-cover-image path/to/article.md
# 指定类型和/或风格
/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 --quick
# 指定宽高比(默认:2.35:1
/baoyu-cover-image path/to/article.md --aspect 16:9
# 指定维度(5D 系统
/baoyu-cover-image path/to/article.md --type conceptual --palette cool --rendering digital
/baoyu-cover-image path/to/article.md --text title-subtitle --mood bold
# 不包含标题文字
# 风格预设(向后兼容的简写方式)
/baoyu-cover-image path/to/article.md --style blueprint
# 指定宽高比(默认:16:9
/baoyu-cover-image path/to/article.md --aspect 2.35:1
# 纯视觉(不含标题文字)
/baoyu-cover-image path/to/article.md --no-title
```
可用类型:`hero``conceptual``typography``metaphor``scene``minimal`
可用风格:`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`
**风格预览**
| | | |
|:---:|:---:|:---:|
| ![elegant](./screenshots/cover-image-styles/elegant.webp) | ![blueprint](./screenshots/cover-image-styles/blueprint.webp) | ![bold-editorial](./screenshots/cover-image-styles/bold-editorial.webp) |
| elegant | blueprint | bold-editorial |
| ![chalkboard](./screenshots/cover-image-styles/chalkboard.webp) | ![dark-atmospheric](./screenshots/cover-image-styles/dark-atmospheric.webp) | ![editorial-infographic](./screenshots/cover-image-styles/editorial-infographic.webp) |
| chalkboard | dark-atmospheric | editorial-infographic |
| ![fantasy-animation](./screenshots/cover-image-styles/fantasy-animation.webp) | ![intuition-machine](./screenshots/cover-image-styles/intuition-machine.webp) | ![minimal](./screenshots/cover-image-styles/minimal.webp) |
| fantasy-animation | intuition-machine | minimal |
| ![nature](./screenshots/cover-image-styles/nature.webp) | ![notion](./screenshots/cover-image-styles/notion.webp) | ![pixel-art](./screenshots/cover-image-styles/pixel-art.webp) |
| nature | notion | pixel-art |
| ![playful](./screenshots/cover-image-styles/playful.webp) | ![retro](./screenshots/cover-image-styles/retro.webp) | ![sketch-notes](./screenshots/cover-image-styles/sketch-notes.webp) |
| playful | retro | sketch-notes |
| ![vector-illustration](./screenshots/cover-image-styles/vector-illustration.webp) | ![vintage](./screenshots/cover-image-styles/vintage.webp) | ![warm](./screenshots/cover-image-styles/warm.webp) |
| vector-illustration | vintage | warm |
| ![watercolor](./screenshots/cover-image-styles/watercolor.webp) | ![flat-doodle](./screenshots/cover-image-styles/flat-doodle.webp) | |
| watercolor | flat-doodle | |
**五个维度**
- **类型 (Type)**`hero``conceptual``typography``metaphor``scene``minimal`
- **配色 (Palette)**`warm``elegant``cool``dark``earth``vivid``pastel``mono``retro`
- **渲染 (Rendering)**`flat-vector``hand-drawn``painterly``digital``pixel``chalk`
- **文字 (Text)**`none``title-only`(默认)、`title-subtitle``text-rich`
- **氛围 (Mood)**`subtle``balanced`(默认)、`bold`
#### baoyu-slide-deck
@@ -553,7 +543,7 @@ AI 驱动的生成后端。
#### baoyu-image-gen
基于 AI SDK 的图像生成,使用官方 OpenAIGoogle API。支持文生图、参考图、宽高比和质量预设。
基于 AI SDK 的图像生成,使用官方 OpenAIGoogle 和 DashScope(阿里通义万相)API。支持文生图、参考图、宽高比和质量预设。
```bash
# 基础生成(自动检测服务商)
@@ -568,6 +558,9 @@ AI 驱动的生成后端。
# 指定服务商
/baoyu-image-gen --prompt "一只猫" --image cat.png --provider openai
# DashScope(阿里通义万相)
/baoyu-image-gen --prompt "一只可爱的猫" --image cat.png --provider dashscope
# 带参考图(仅 Google 多模态支持)
/baoyu-image-gen --prompt "把它变成蓝色" --image out.png --ref source.png
```
@@ -578,7 +571,7 @@ AI 驱动的生成后端。
| `--prompt`, `-p` | 提示词文本 |
| `--promptfiles` | 从文件读取提示词(多文件拼接) |
| `--image` | 输出图片路径(必需) |
| `--provider` | `google``openai`(默认:google |
| `--provider` | `google``openai``dashscope`(默认:google |
| `--model`, `-m` | 模型 ID |
| `--ar` | 宽高比(如 `16:9``1:1``4:3` |
| `--size` | 尺寸(如 `1024x1024` |
@@ -590,15 +583,18 @@ AI 驱动的生成后端。
|------|------|--------|
| `OPENAI_API_KEY` | OpenAI API 密钥 | - |
| `GOOGLE_API_KEY` | Google API 密钥 | - |
| `DASHSCOPE_API_KEY` | DashScope API 密钥(阿里云) | - |
| `OPENAI_IMAGE_MODEL` | OpenAI 模型 | `gpt-image-1.5` |
| `GOOGLE_IMAGE_MODEL` | Google 模型 | `gemini-3-pro-image-preview` |
| `DASHSCOPE_IMAGE_MODEL` | DashScope 模型 | `z-image-turbo` |
| `OPENAI_BASE_URL` | 自定义 OpenAI 端点 | - |
| `GOOGLE_BASE_URL` | 自定义 Google 端点 | - |
| `DASHSCOPE_BASE_URL` | 自定义 DashScope 端点 | - |
**服务商自动选择**
1. 如果指定了 `--provider` → 使用指定的
2. 如果只有一个 API 密钥 → 使用对应服务商
3. 如果两个都有 → 默认使用 Google
3. 如果多个可用 → 默认使用 Google
#### baoyu-danger-gemini-web
@@ -709,6 +705,11 @@ OPENAI_IMAGE_MODEL=gpt-image-1.5
GOOGLE_API_KEY=xxx
GOOGLE_IMAGE_MODEL=gemini-3-pro-image-preview
# GOOGLE_BASE_URL=https://generativelanguage.googleapis.com/v1beta
# DashScope(阿里通义万相)
DASHSCOPE_API_KEY=sk-xxx
DASHSCOPE_IMAGE_MODEL=z-image-turbo
# DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/api/v1
EOF
```
@@ -737,13 +738,14 @@ mkdir -p .baoyu-skills/baoyu-cover-image
然后创建 `.baoyu-skills/baoyu-cover-image/EXTEND.md`
```markdown
## 自定义风格
## 自定义配色
### brand
- 主色:#1a73e8
- 色:#34a853
- 字体风格:现代无衬线
- 始终包含公司 logo 水印
### corporate-tech
- 主色:#1a73e8#4A90D9
- 背景色:#F5F7FA
- 强调色:#00B4D8#48CAE4
- 装饰提示:简洁线条、渐变效果
- 适用于:SaaS、企业、技术内容
```
扩展内容会在技能执行前加载,并覆盖默认设置。
+178 -302
View File
@@ -7,236 +7,201 @@ description: Analyzes article structure, identifies positions requiring visual a
Analyze articles, identify illustration positions, generate images with Type × Style consistency.
## Usage
```bash
# Auto-select type and style based on content
/baoyu-article-illustrator path/to/article.md
# Specify type
/baoyu-article-illustrator path/to/article.md --type infographic
# Specify style
/baoyu-article-illustrator path/to/article.md --style blueprint
# Combine type and style
/baoyu-article-illustrator path/to/article.md --type flowchart --style notion
# Specify density
/baoyu-article-illustrator path/to/article.md --density rich
# Direct content input
/baoyu-article-illustrator
[paste content]
```
## Options
| Option | Description |
|--------|-------------|
| `--type <name>` | Illustration type (see Type Gallery) |
| `--style <name>` | Visual style (see Style Gallery) |
| `--density <level>` | Image count: minimal / balanced / rich |
## Two Dimensions
| Dimension | Controls | Examples |
|-----------|----------|----------|
| **Type** | Information structure, content layout | infographic, scene, flowchart, comparison, framework, timeline |
| **Style** | Visual aesthetics, colors, mood | notion, warm, minimal, blueprint, watercolor, elegant |
| **Type** | Information structure, layout | infographic, scene, flowchart, comparison, framework, timeline |
| **Style** | Visual aesthetics, mood | notion, warm, minimal, blueprint, watercolor, elegant |
Type × Style can be freely combined. Example: `--type infographic --style blueprint` creates technical data visualization with schematic aesthetics.
Type × Style can be freely combined. Example: `--type infographic --style blueprint`
## Type Gallery
| Type | Description | Best For |
|------|-------------|----------|
| `infographic` | Data visualization, charts, metrics | Technical articles, data analysis, comparisons |
| `scene` | Atmospheric illustration, mood rendering | Narrative articles, personal stories, emotional content |
| `flowchart` | Process diagrams, step visualization | Tutorials, workflows, decision trees |
| `comparison` | Side-by-side, before/after contrast | Product comparisons, option evaluations |
| `framework` | Concept maps, relationship diagrams | Methodologies, models, architecture design |
| `timeline` | Chronological progression | History, project progress, evolution |
## Density Options
| Density | Count | Description |
|---------|-------|-------------|
| `minimal` | 1-2 | Core concepts only |
| `balanced` (Default) | 3-5 | Major sections coverage |
| `rich` | 6+ | Rich visual support |
| Type | Best For |
|------|----------|
| `infographic` | Data, metrics, technical articles |
| `scene` | Narratives, personal stories, emotional content |
| `flowchart` | Tutorials, workflows, processes |
| `comparison` | Side-by-side, before/after, options |
| `framework` | Methodologies, models, architecture |
| `timeline` | History, progress, evolution |
## Style Gallery
| Style | Description | Best For |
|-------|-------------|----------|
| `notion` (Default) | Minimalist hand-drawn line art | Knowledge sharing, SaaS, productivity |
| `elegant` | Refined, sophisticated | Business, thought leadership |
| `warm` | Friendly, approachable | Personal growth, lifestyle, education |
| `minimal` | Ultra-clean, zen-like | Philosophy, minimalism, core concepts |
| `blueprint` | Technical schematics | Architecture, system design, engineering |
| `watercolor` | Soft artistic with natural warmth | Lifestyle, travel, creative |
| `editorial` | Magazine-style infographic | Tech explainers, journalism |
| `scientific` | Academic precise diagrams | Biology, chemistry, technical research |
| Style | Best For |
|-------|----------|
| `notion` (Default) | Knowledge sharing, SaaS, productivity |
| `elegant` | Business, thought leadership |
| `warm` | Personal growth, lifestyle, education |
| `minimal` | Philosophy, core concepts |
| `blueprint` | Architecture, system design |
| `watercolor` | Lifestyle, travel, creative |
| `editorial` | Tech explainers, journalism |
| `scientific` | Academic, technical research |
Full style specifications: `references/styles/<style>.md`
## Type × Style Compatibility
| | notion | warm | minimal | blueprint | watercolor | elegant | editorial | scientific |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| infographic | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
| scene | ✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ | ✗ |
| flowchart | ✓✓ | ✓ | ✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ |
| comparison | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ |
| framework | ✓✓ | ✓ | ✓✓ | ✓✓ | ✗ | ✓✓ | ✓ | ✓✓ |
| timeline | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
Full styles: [references/styles.md](references/styles.md)
## Auto Selection
| Content Signals | Recommended Type | Recommended Style |
|-----------------|------------------|-------------------|
| API, metrics, data, comparison, numbers | infographic | blueprint, notion |
| Story, emotion, journey, experience, personal | scene | warm, watercolor |
| How-to, steps, workflow, process, tutorial | flowchart | notion, minimal |
| vs, pros/cons, before/after, alternatives | comparison | notion, elegant |
| Framework, model, architecture, principles | framework | blueprint, notion |
| History, timeline, progress, evolution | timeline | elegant, warm |
## Output Directory
```
illustrations/{topic-slug}/
├── source-{slug}.{ext}
├── outline.md
├── prompts/
│ └── illustration-{slug}.md
└── NN-{type}-{slug}.png
```
**Slug**: Extract 2-4 word topic in kebab-case.
**Conflict**: Append `-YYYYMMDD-HHMMSS` if exists.
| Content Signals | Type | Style |
|-----------------|------|-------|
| API, metrics, data, numbers | infographic | blueprint, notion |
| Story, emotion, journey | scene | warm, watercolor |
| How-to, steps, workflow | flowchart | notion, minimal |
| vs, pros/cons, before/after | comparison | notion, elegant |
| Framework, model, architecture | framework | blueprint, notion |
| History, timeline, progress | timeline | elegant, warm |
## Workflow
### Progress
Copy this checklist and track progress:
```
- [ ] Step 1: Setup & Analyze
- [ ] Step 2: Confirm Settings ⚠️ REQUIRED
- [ ] Step 3: Generate Outline
- [ ] Step 4: Generate Images
- [ ] Step 5: Finalize
Progress:
- [ ] Step 1: Pre-check
- [ ] Step 2: Setup & Analyze
- [ ] Step 3: Confirm Settings ⚠️ REQUIRED
- [ ] Step 4: Generate Outline
- [ ] Step 5: Generate Images
- [ ] Step 6: Finalize
```
### Step 1: Setup & Analyze
---
**1.1 Load Preferences (EXTEND.md)**
### Step 1: Pre-check
Use Bash to check EXTEND.md existence (priority order):
**1.1 Determine Input Type**
| Input | Output Directory | Next |
|-------|------------------|------|
| File path | Ask user (1.2) | → 1.2 |
| Pasted content | `illustrations/{topic-slug}/` | → 1.4 |
**1.2 Determine Output Directory** (file path input only)
Check `default_output_dir` in preferences:
| Preference Value | Action |
|------------------|--------|
| `same-dir` | Use `{article-dir}/`, display "Output: {path}" |
| `imgs-subdir` | Use `{article-dir}/imgs/`, display "Output: {path}" |
| `illustrations-subdir` | Use `{article-dir}/illustrations/`, display "Output: {path}" |
| `independent` | Use `illustrations/{topic-slug}/`, display "Output: {path}" |
| Not configured | **MUST** ask with AskUserQuestion ↓ |
**AskUserQuestion** (when no preference):
- `{article-dir}/` - Same directory as article
- `{article-dir}/imgs/` - Images subdirectory
- `{article-dir}/illustrations/` - Illustrations subdirectory (Recommended)
- `illustrations/{topic-slug}/` - Independent directory
- Save as default - Remember this choice for future runs
**1.3 Check Existing Images**
Scan target directory for `.png/.jpg/.webp` files.
If images exist → AskUserQuestion: How to handle?
- `supplement` - Keep existing, generate only new positions
- `overwrite` - Overwrite same-name files
- `regenerate` - Clear all and regenerate
**1.4 Confirm Article Update Method** (file path input only)
AskUserQuestion: How to update article?
- `update` - Modify original file directly
- `copy` - Create `{name}-illustrated.md` copy
**1.5 Load Preferences (EXTEND.md)**
```bash
# Check project-level first
test -f .baoyu-skills/baoyu-article-illustrator/EXTEND.md && echo "project"
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
test -f "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "user"
```
┌──────────────────────────────────────────────────────────┬───────────────────┐
│ Path │ Location │
├──────────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-article-illustrator/EXTEND.md │ Project directory │
├──────────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md │ User home │
└──────────────────────────────────────────────────────────┴───────────────────┘
| Result | Action |
|--------|--------|
| Found | Read, parse, display summary |
| Not found | Ask with AskUserQuestion (see references/config/first-time-setup.md) |
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, display summary │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Ask user with AskUserQuestion (see references/config/first-time-setup.md) │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**Supports**: Watermark | Preferred type/style | Custom styles | Language | Output directory
**EXTEND.md Supports**: Watermark | Preferred type/style | Custom style definitions | Language preference
---
Schema: `references/config/preferences-schema.md`
### Step 2: Setup & Analyze
**1.2 Analyze Content**
Read article, detect language, classify content.
**2.1 Analyze Content**
| Analysis | Description |
|----------|-------------|
| Content type | Technical / Tutorial / Methodology / Narrative |
| Core arguments | 2-5 main points that MUST be visualized |
| Core arguments | 2-5 main points to visualize |
| Visual opportunities | Positions where illustrations add value |
| Recommended type | Based on content signals |
| Recommended density | Based on article length and complexity |
| Recommended density | Based on length and complexity |
**1.3 Extract Core Arguments**
**2.2 Extract Core Arguments**
Extract 2-5 core arguments that MUST be visualized:
- Main thesis
- Key concepts reader needs
- Comparisons/contrasts being made
- Comparisons/contrasts
- Framework/model proposed
**CRITICAL**: If article uses metaphors (e.g., "电锯切西瓜"), do NOT illustrate literally. Visualize the **underlying concept** instead.
**CRITICAL**: If article uses metaphors (e.g., "电锯切西瓜"), do NOT illustrate literally. Visualize the **underlying concept**.
**1.4 Identify Positions**
**2.3 Identify Positions**
**What to Illustrate**:
**Illustrate**:
- Core arguments (REQUIRED)
- Abstract concepts needing visualization
- Data comparisons, metrics
- Abstract concepts
- Data comparisons
- Processes, workflows
**What NOT to Illustrate**:
**Do NOT Illustrate**:
- Metaphors literally
- Decorative scenes without information
- Decorative scenes
- Generic illustrations
### Step 2: Confirm Settings ⚠️
---
### Step 3: Confirm Settings ⚠️
**Do NOT skip.** Use AskUserQuestion with 3-4 questions in ONE call.
**Question 1: Illustration Type**
**Q1: Illustration Type**
- [Recommended based on analysis] (Recommended)
- infographic / scene / flowchart / comparison / framework / timeline / mixed
Based on content analysis, recommend type:
- [Recommended type based on signals] (Recommended)
- infographic - Data visualization, charts
- scene - Atmospheric, mood rendering
- flowchart - Process, steps
- comparison - Side-by-side contrast
- framework - Concept relationships
- timeline - Chronological progression
- mixed - Combine multiple types
**Q2: Density**
- minimal (1-2) - Core concepts only
- balanced (3-5) (Recommended) - Major sections
- rich (6+) - Comprehensive support
**Question 2: Density**
**Q3: Style** (ALWAYS ask, even with preferred_style in EXTEND.md)
- minimal (1-2 images) - Core concepts only
- balanced (3-5 images) (Recommended) - Major sections
- rich (6+ images) - Comprehensive visual support
If EXTEND.md has `preferred_style`:
- [Custom style name + brief description] (Recommended)
- [Top compatible built-in style 1]
- [Top compatible built-in style 2]
- [Top compatible built-in style 3]
**Question 3: Style**
If no `preferred_style`:
- [Best compatible from matrix] (Recommended)
- [Other ✓✓ style 1]
- [Other ✓✓ style 2]
- [Other ✓ style]
Based on recommended Type, suggest compatible styles (see Type × Style Compatibility matrix):
- [Best compatible style for recommended type] (Recommended)
- [Other highly compatible styles: ✓✓ from matrix]
- [Compatible styles: ✓ from matrix]
Style selection based on Type × Style compatibility matrix (references/styles.md).
Full specs: `references/styles/<style>.md`
**Question 4** (only if source ≠ user language):
- Language: Source language / User language
**Q4** (only if source ≠ user language):
- Language: Source / User language
### Step 3: Generate Outline
---
Based on confirmed Type + Density + Style, generate illustration outline.
### Step 4: Generate Outline
**Outline Format** (`outline.md`):
Save as `outline.md`:
```yaml
---
@@ -249,44 +214,48 @@ image_count: 4
## Illustration 1
**Position**: [section] / [paragraph]
**Purpose**: [why this illustration helps]
**Purpose**: [why this helps]
**Visual Content**: [what to show]
**Type Application**: [how type applies here]
**Type Application**: [how type applies]
**Filename**: 01-infographic-concept-name.png
## Illustration 2
...
```
**Outline Requirements**:
- Each illustration position justified by content needs
- Type applied consistently across all illustrations
- Style characteristics reflected in visual descriptions
- Count matches density selection
**Requirements**:
- Each position justified by content needs
- Type applied consistently
- Style reflected in descriptions
- Count matches density
### Step 4: Generate Images
---
**4.1 Create Prompts**
### Step 5: Generate Images
Follow Prompt Construction principles below. Save each to `prompts/illustration-{slug}.md`.
**5.1 Create Prompts**
**4.2 Select Generation Skill**
Follow [references/prompt-construction.md](references/prompt-construction.md). Save to `prompts/illustration-{slug}.md`.
Check available image generation skills. If multiple, ask user to choose.
**5.2 Select Generation Skill**
**4.3 Apply Watermark** (if enabled in preferences)
Check available skills. If multiple, ask user.
Add to prompt: `Include a subtle watermark "[content]" positioned at [position] with approximately [opacity*100]% visibility.`
**5.3 Apply Watermark** (if enabled)
**4.4 Generate**
Add: `Include a subtle watermark "[content]" at [position].`
**5.4 Generate**
1. Generate sequentially
2. After each: "Generated X/N"
3. On failure: auto-retry once, then log and continue
3. On failure: retry once, then log and continue
### Step 5: Finalize
---
**5.1 Update Article**
### Step 6: Finalize
**6.1 Update Article**
Insert after corresponding paragraph:
```markdown
@@ -295,158 +264,65 @@ Insert after corresponding paragraph:
Alt text: concise description in article's language.
**5.2 Output Summary**
**6.2 Output Summary**
```
Article Illustration Complete!
Article: [path]
Type: [type name]
Density: [minimal/balanced/rich]
Style: [style name]
Location: [directory path]
Type: [type] | Density: [level] | Style: [style]
Location: [directory]
Images: X/N generated
Positions:
- 01-infographic-xxx.png → After "[Section]"
- 02-infographic-yyy.png → After "[Section]"
- 01-xxx.png → After "[Section]"
- 02-yyy.png → After "[Section]"
[If failures]
Failed:
- NN-type-zzz.png: [reason]
- NN-zzz.png: [reason]
```
## Prompt Construction
---
### Principles
## Output Directory
Good prompts must include:
1. **Layout Structure First**: Describe composition, zones, flow direction
2. **Specific Data/Labels**: Use actual numbers, terms from article
3. **Visual Relationships**: How elements connect
4. **Semantic Colors**: Meaning-based color choices (red=warning, green=efficient)
5. **Style Characteristics**: Line treatment, texture, mood
6. **Aspect Ratio**: End with ratio and complexity level
### Type-Specific Prompts
**Infographic**:
```
[Title] - Data Visualization
Layout: [grid/radial/hierarchical]
ZONES:
- Zone 1: [data point with specific values]
- Zone 2: [comparison with metrics]
- Zone 3: [summary/conclusion]
LABELS: [specific numbers, percentages, terms from article]
COLORS: [semantic color mapping]
STYLE: [style characteristics]
ASPECT: 16:9
illustrations/{topic-slug}/
├── source-{slug}.{ext}
├── outline.md
├── prompts/
│ └── illustration-{slug}.md
└── NN-{type}-{slug}.png
```
**Scene**:
```
[Title] - Atmospheric Scene
FOCAL POINT: [main subject]
ATMOSPHERE: [lighting, mood, environment]
MOOD: [emotion to convey]
COLOR TEMPERATURE: [warm/cool/neutral]
STYLE: [style characteristics]
ASPECT: 16:9
```
**Flowchart**:
```
[Title] - Process Flow
Layout: [left-right/top-down/circular]
STEPS:
1. [Step name] - [brief description]
2. [Step name] - [brief description]
...
CONNECTIONS: [arrow types, decision points]
STYLE: [style characteristics]
ASPECT: 16:9
```
**Comparison**:
```
[Title] - Comparison View
LEFT SIDE - [Option A]:
- [Point 1]
- [Point 2]
RIGHT SIDE - [Option B]:
- [Point 1]
- [Point 2]
DIVIDER: [visual separator]
STYLE: [style characteristics]
ASPECT: 16:9
```
**Framework**:
```
[Title] - Conceptual Framework
STRUCTURE: [hierarchical/network/matrix]
NODES:
- [Concept 1] - [role]
- [Concept 2] - [role]
RELATIONSHIPS: [how nodes connect]
STYLE: [style characteristics]
ASPECT: 16:9
```
**Timeline**:
```
[Title] - Chronological View
DIRECTION: [horizontal/vertical]
EVENTS:
- [Date/Period 1]: [milestone]
- [Date/Period 2]: [milestone]
MARKERS: [visual indicators]
STYLE: [style characteristics]
ASPECT: 16:9
```
### What to Avoid
- Vague descriptions ("a nice image")
- Literal metaphor illustrations
- Missing concrete labels/annotations
- Generic decorative elements
**Slug**: 2-4 word topic in kebab-case.
**Conflict**: Append `-YYYYMMDD-HHMMSS` if exists.
## Modification
| Action | Steps |
|--------|-------|
| **Edit** | Update prompt → Regenerate → Update reference |
| **Add** | Identify position → Create prompt → Generate → Update outline → Insert reference |
| **Edit** | **Update prompt file FIRST** → Regenerate → Update reference |
| **Add** | Identify position → Create prompt → Generate → Update outline → Insert |
| **Delete** | Delete files → Remove reference → Update outline |
**IMPORTANT**: When updating illustrations, ALWAYS update the prompt file (`prompts/illustration-{slug}.md`) FIRST before regenerating. This ensures:
1. Changes are documented and reproducible
2. The prompt reflects the new requirements
3. Future regeneration uses the correct prompt
## References
| File | Content |
|------|---------|
| [references/styles.md](references/styles.md) | Style gallery & compatibility matrix |
| [references/usage.md](references/usage.md) | Command syntax and options |
| [references/styles.md](references/styles.md) | Style gallery & compatibility |
| [references/prompt-construction.md](references/prompt-construction.md) | Prompt templates |
| `references/styles/<style>.md` | Full style specifications |
| `references/config/preferences-schema.md` | EXTEND.md schema |
| `references/config/first-time-setup.md` | First-time setup flow |
## Extension Support
Custom configurations via EXTEND.md. See **Step 1.1** for paths and supported options.
Custom configurations via EXTEND.md. See **Step 1.5** for paths and supported options.
@@ -15,7 +15,6 @@ watermark:
enabled: false
content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
opacity: 0.7 # 0.1-1.0
preferred_style:
name: null # Built-in or custom style name
@@ -23,6 +22,8 @@ preferred_style:
language: null # zh|en|ja|ko|auto
default_output_dir: null # same-dir|illustrations-subdir|independent
custom_styles:
- name: my-style
description: "Style description"
@@ -44,10 +45,10 @@ custom_styles:
| `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_style.name` | string | null | Style name or null |
| `preferred_style.description` | string | "" | Custom notes/override |
| `language` | string | null | Output language (null = auto-detect) |
| `default_output_dir` | enum | null | Output directory preference (null = ask each time) |
| `custom_styles` | array | [] | User-defined styles |
## Position Options
@@ -59,6 +60,14 @@ custom_styles:
| `bottom-center` | Bottom center |
| `top-right` | Upper right corner |
## Output Directory Options
| Value | Description |
|-------|-------------|
| `same-dir` | Same directory as article |
| `illustrations-subdir` | `{article-dir}/illustrations/` subdirectory |
| `independent` | `illustrations/{topic-slug}/` in working directory |
## Custom Style Fields
| Field | Required | Description |
@@ -94,7 +103,6 @@ watermark:
enabled: true
content: "@myaccount"
position: bottom-right
opacity: 0.5
preferred_style:
name: notion
@@ -0,0 +1,127 @@
# Prompt Construction
## Principles
Good prompts must include:
1. **Layout Structure First**: Describe composition, zones, flow direction
2. **Specific Data/Labels**: Use actual numbers, terms from article
3. **Visual Relationships**: How elements connect
4. **Semantic Colors**: Meaning-based color choices (red=warning, green=efficient)
5. **Style Characteristics**: Line treatment, texture, mood
6. **Aspect Ratio**: End with ratio and complexity level
## Type-Specific Templates
### Infographic
```
[Title] - Data Visualization
Layout: [grid/radial/hierarchical]
ZONES:
- Zone 1: [data point with specific values]
- Zone 2: [comparison with metrics]
- Zone 3: [summary/conclusion]
LABELS: [specific numbers, percentages, terms from article]
COLORS: [semantic color mapping]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Scene
```
[Title] - Atmospheric Scene
FOCAL POINT: [main subject]
ATMOSPHERE: [lighting, mood, environment]
MOOD: [emotion to convey]
COLOR TEMPERATURE: [warm/cool/neutral]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Flowchart
```
[Title] - Process Flow
Layout: [left-right/top-down/circular]
STEPS:
1. [Step name] - [brief description]
2. [Step name] - [brief description]
...
CONNECTIONS: [arrow types, decision points]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Comparison
```
[Title] - Comparison View
LEFT SIDE - [Option A]:
- [Point 1]
- [Point 2]
RIGHT SIDE - [Option B]:
- [Point 1]
- [Point 2]
DIVIDER: [visual separator]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Framework
```
[Title] - Conceptual Framework
STRUCTURE: [hierarchical/network/matrix]
NODES:
- [Concept 1] - [role]
- [Concept 2] - [role]
RELATIONSHIPS: [how nodes connect]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Timeline
```
[Title] - Chronological View
DIRECTION: [horizontal/vertical]
EVENTS:
- [Date/Period 1]: [milestone]
- [Date/Period 2]: [milestone]
MARKERS: [visual indicators]
STYLE: [style characteristics]
ASPECT: 16:9
```
## What to Avoid
- Vague descriptions ("a nice image")
- Literal metaphor illustrations
- Missing concrete labels/annotations
- Generic decorative elements
## Watermark Integration
If watermark enabled in preferences, append:
```
Include a subtle watermark "[content]" positioned at [position] with approximately [opacity*100]% visibility.
```
@@ -0,0 +1,66 @@
# Usage
## Command Syntax
```bash
# Auto-select type and style based on content
/baoyu-article-illustrator path/to/article.md
# Specify type
/baoyu-article-illustrator path/to/article.md --type infographic
# Specify style
/baoyu-article-illustrator path/to/article.md --style blueprint
# Combine type and style
/baoyu-article-illustrator path/to/article.md --type flowchart --style notion
# Specify density
/baoyu-article-illustrator path/to/article.md --density rich
# Direct content input (paste mode)
/baoyu-article-illustrator
[paste content]
```
## Options
| Option | Description |
|--------|-------------|
| `--type <name>` | Illustration type (see Type Gallery in SKILL.md) |
| `--style <name>` | Visual style (see references/styles.md) |
| `--density <level>` | Image count: minimal / balanced / rich |
## Input Modes
| Mode | Trigger | Output Directory |
|------|---------|------------------|
| File path | `path/to/article.md` | Use `default_output_dir` preference, or ask if not set |
| Paste content | No path argument | `illustrations/{topic-slug}/` |
## Output Directory Options
| Value | Path |
|-------|------|
| `same-dir` | `{article-dir}/` |
| `illustrations-subdir` | `{article-dir}/illustrations/` |
| `independent` | `illustrations/{topic-slug}/` |
Configure in EXTEND.md: `default_output_dir: illustrations-subdir`
## Examples
**Technical article with data**:
```bash
/baoyu-article-illustrator api-design.md --type infographic --style blueprint
```
**Personal story**:
```bash
/baoyu-article-illustrator journey.md --type scene --style warm
```
**Tutorial with steps**:
```bash
/baoyu-article-illustrator how-to-deploy.md --type flowchart --density rich
```
+140 -377
View File
@@ -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,163 @@ 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
```
**Compress character sheet** (recommended):
Compress to reduce token usage when used as reference image:
- Use available image compression skill (if any)
- Or system tools: `pngquant`, `optipng`, `sips` (macOS)
- **Keep PNG format**, lossless compression preferred
**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]
```
Schema: [references/config/preferences-schema.md](references/config/preferences-schema.md)
**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
## References
**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`
**Core Templates**:
- [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
**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
**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)
### Step 6: Merge to PDF
**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
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
```
**Config**:
- [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
## 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 |
| **Edit** | **Update prompt file FIRST**`--regenerate N` → Regenerate PDF |
| **Add** | Create prompt at position → Generate with character ref → Renumber subsequent → Update storyboard → Regenerate PDF |
| **Delete** | Remove files → Renumber subsequent → 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.
## 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
**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)
**Config**:
- `config/preferences-schema.md` - EXTEND.md schema
- `config/first-time-setup.md` - First-time setup flow
- `config/watermark-guide.md` - Watermark configuration
**IMPORTANT**: When updating pages, ALWAYS update the prompt file (`prompts/NN-{cover|page}-[slug].md`) FIRST before regenerating. This ensures changes are documented and reproducible.
## 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
@@ -15,7 +15,6 @@ watermark:
enabled: false
content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
opacity: 0.5 # 0.1-1.0 (lower default for comics)
preferred_art: null # ligne-claire|manga|realistic|ink-brush|chalk
preferred_tone: null # neutral|warm|dramatic|romantic|energetic|vintage|action
@@ -42,7 +41,6 @@ character_presets:
| `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.5 | Transparency (0.1-1.0, lower for comics) |
| `preferred_art` | string | null | Art style (ligne-claire, manga, realistic, ink-brush, chalk) |
| `preferred_tone` | string | null | Tone (neutral, warm, dramatic, romantic, energetic, vintage, action) |
| `preferred_layout` | string | null | Layout preference or null |
@@ -113,7 +111,6 @@ watermark:
enabled: true
content: "@comicstudio"
position: bottom-right
opacity: 0.4
preferred_art: manga
preferred_tone: neutral
@@ -28,19 +28,6 @@ description: Watermark configuration guide for baoyu-comic
| `bottom-center` | Webtoon vertical scroll, centered designs | Text-heavy bottom area |
| `top-right` | **Not recommended for comics** | Always - conflicts with page numbers |
## Opacity for Comics
Comics typically use lower opacity than infographics to avoid disrupting panel flow:
| Opacity | Visual Effect | Use Case |
|---------|---------------|----------|
| 0.3 | Very subtle, barely visible | **Recommended for comics** |
| 0.4 | Light presence | Balance of visibility and subtlety |
| 0.5 | Noticeable | Standard comics default |
| 0.6+ | Strong presence | Not recommended (distracting) |
**Default**: 0.5 (compared to 0.7 for infographics)
## Content Format
| Format | Example | Style |
@@ -63,10 +50,9 @@ Comics typically use lower opacity than infographics to avoid disrupting panel f
When watermark is enabled, add to 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.
Include a subtle watermark "[content]" positioned at [position].
The watermark should be legible but not distracting from the comic panels
and storytelling. Ensure watermark does not overlap speech bubbles or key action.
```
## Common Issues
@@ -76,5 +62,5 @@ Ensure watermark does not overlap speech bubbles or key action.
| Watermark invisible on dark panels | Adjust contrast or add subtle outline |
| Watermark overlaps speech bubble | Change position or lower on page |
| Watermark inconsistent across pages | Use session ID for consistency |
| Watermark too prominent | Decrease opacity (0.3-0.4 for comics) |
| Watermark too prominent | Change position or reduce size |
| Conflicts with page number | Never use top-right position |
@@ -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
+505
View File
@@ -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
+3 -1
View File
@@ -147,7 +147,9 @@ async function processFile(
const outputSize = statSync(tempOutput).size;
if (!opts.keep && absInput !== output) {
unlinkSync(absInput);
const ext = extname(absInput);
const base = absInput.slice(0, -ext.length);
renameSync(absInput, `${base}_original${ext}`);
}
renameSync(tempOutput, output);
+140 -348
View File
@@ -1,23 +1,28 @@
---
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 5 dimensions (type, palette, rendering, text, mood) combining 9 color palettes and 6 rendering 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 5-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 (new 5D system)
/baoyu-cover-image article.md --type conceptual --palette warm --rendering flat-vector
/baoyu-cover-image article.md --text title-subtitle --mood bold
# Style presets (backward-compatible shorthand for palette + rendering)
/baoyu-cover-image article.md --style blueprint
/baoyu-cover-image article.md --style blueprint --rendering hand-drawn # override rendering
# Visual only (no title text)
/baoyu-cover-image article.md --no-title
@@ -27,7 +32,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 --palette mono --rendering digital --aspect 1:1 --quick
[paste content]
```
@@ -35,20 +40,28 @@ Generate elegant cover images for articles with multiple style options.
| Option | Description |
|--------|-------------|
| `--type <name>` | Cover type (see Type Gallery) |
| `--style <name>` | Cover style (see Style Gallery) |
| `--aspect <ratio>` | 2.35:1 (default), 16:9, 1:1 |
| `--type <name>` | Cover type: hero, conceptual, typography, metaphor, scene, minimal |
| `--palette <name>` | Color palette: warm, elegant, cool, dark, earth, vivid, pastel, mono, retro |
| `--rendering <name>` | Rendering style: flat-vector, hand-drawn, painterly, digital, pixel, chalk |
| `--style <name>` | Preset shorthand (expands to palette + rendering, see [Style Presets](references/style-presets.md)) |
| `--text <level>` | Text density: none, title-only, title-subtitle, text-rich |
| `--mood <level>` | Emotional intensity: subtle, balanced, bold |
| `--aspect <ratio>` | 16:9 (default), 2.35:1, 4:3, 3:2, 1:1, 3:4 |
| `--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
## Five 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 |
| **Palette** | Colors, color scheme, decorative hints | warm, elegant, cool, dark, earth, vivid, pastel, mono, retro | auto |
| **Rendering** | Line quality, texture, depth, element style | flat-vector, hand-drawn, painterly, digital, pixel, chalk | 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. Auto-selection rules: [references/auto-selection.md](references/auto-selection.md)
## Type Gallery
@@ -61,121 +74,91 @@ Type × Style can be freely combined. Example: `--type conceptual --style bluepr
| `scene` | Atmospheric scene, narrative feel | Stories, travel, lifestyle |
| `minimal` | Minimalist composition, generous whitespace | Zen, focus, core concepts |
## Auto Type Selection
Type composition details: [references/types.md](references/types.md)
When `--type` is omitted, select based on content signals:
## Palette Gallery
| Signals | Type |
|---------|------|
| Product, launch, announcement, release, reveal | `hero` |
| Architecture, framework, system, API, technical, model | `conceptual` |
| Quote, opinion, insight, thought, headline, statement | `typography` |
| Philosophy, growth, abstract, meaning, reflection | `metaphor` |
| Story, journey, travel, lifestyle, experience, narrative | `scene` |
| Zen, focus, essential, core, simple, pure | `minimal` |
| Palette | Vibe | Primary Colors |
|---------|------|----------------|
| `warm` | Friendly, approachable | Orange, golden yellow, terracotta |
| `elegant` | Sophisticated, refined | Soft coral, muted teal, dusty rose |
| `cool` | Technical, professional | Engineering blue, navy, cyan |
| `dark` | Cinematic, premium | Electric purple, cyan, magenta |
| `earth` | Natural, organic | Forest green, sage, earth brown |
| `vivid` | Energetic, bold | Bright red, neon green, electric blue |
| `pastel` | Gentle, whimsical | Soft pink, mint, lavender |
| `mono` | Clean, focused | Black, near-black, white |
| `retro` | Nostalgic, vintage | Muted orange, dusty pink, maroon |
## Style Gallery
Palette definitions: [references/palettes/](references/palettes/)
| Style | Description |
|-------|-------------|
| `elegant` (default) | Refined, sophisticated |
| `blueprint` | Technical schematics |
| `bold-editorial` | Magazine impact |
| `chalkboard` | Chalk on blackboard |
| `dark-atmospheric` | Cinematic dark mode |
| `editorial-infographic` | Visual storytelling |
| `fantasy-animation` | Ghibli/Disney inspired |
| `flat-doodle` | Pastel, cute shapes |
| `intuition-machine` | Technical, bilingual |
| `minimal` | Ultra-clean, zen |
| `nature` | Organic, earthy |
| `notion` | SaaS dashboard |
| `pixel-art` | Retro 8-bit |
| `playful` | Fun, whimsical |
| `retro` | Halftone, vintage |
| `sketch-notes` | Hand-drawn, warm |
| `vector-illustration` | Flat vector |
| `vintage` | Aged, expedition |
| `warm` | Friendly, human |
| `watercolor` | Soft hand-painted |
## Rendering Gallery
Style definitions: [references/styles/](references/styles/)
| Rendering | Description | Key Characteristics |
|-----------|-------------|---------------------|
| `flat-vector` | Clean modern vector | Uniform outlines, flat fills, geometric icons |
| `hand-drawn` | Sketchy organic illustration | Imperfect strokes, paper texture, doodles |
| `painterly` | Soft watercolor/paint | Brush strokes, color bleeds, soft edges |
| `digital` | Polished modern digital | Precise edges, subtle gradients, UI components |
| `pixel` | Retro 8-bit pixel art | Pixel grid, dithering, chunky shapes |
| `chalk` | Chalk on blackboard | Chalk strokes, dust effects, board texture |
## Type × Style Compatibility
Rendering definitions: [references/renderings/](references/renderings/)
| | elegant | blueprint | notion | warm | minimal | watercolor | bold-editorial | dark-atmospheric |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| hero | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
| conceptual | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✗ | ✓ | ✓ |
| typography | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ |
| scene | ✓ | ✗ | ✗ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ |
| minimal | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✗ | ✓ |
## Text & Mood
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
| Text Level | 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 |
## Auto Style Selection
| Mood | 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 |
When `--style` is omitted, select based on content signals:
Full guides: [references/dimensions/text.md](references/dimensions/text.md) | [references/dimensions/mood.md](references/dimensions/mood.md)
| Signals | Style |
|---------|-------|
| Architecture, system design | `blueprint` |
| Product launch, marketing | `bold-editorial` |
| Education, tutorial | `chalkboard` |
| Entertainment, premium | `dark-atmospheric` |
| Tech explainer, research | `editorial-infographic` |
| Fantasy, children | `fantasy-animation` |
| Technical docs, bilingual | `intuition-machine` |
| Personal story, emotion | `warm` |
| Zen, focus, essential | `minimal` |
| Fun, beginner, casual | `playful` |
| Nature, wellness, eco | `nature` |
| SaaS, dashboard | `notion` |
| Workflow, productivity | `flat-doodle` |
| Gaming, retro tech | `pixel-art` |
| Knowledge sharing | `sketch-notes` |
| Creative proposals | `vector-illustration` |
| History, exploration | `vintage` |
| Lifestyle, travel | `watercolor` |
| Business, professional | `elegant` |
## Style Presets & Compatibility
- **Style Presets**: `--style X` expands to palette + rendering. See [references/style-presets.md](references/style-presets.md)
- **Compatibility Matrices**: Palette×Rendering, Type×Rendering, Type×Text, Type×Mood. See [references/compatibility.md](references/compatibility.md)
- ✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## File Structure
Each session creates an independent directory named by content slug:
Output directory depends on `default_output_dir` preference:
| Preference | Output Path |
|------------|-------------|
| `same-dir` | `{article-dir}/` |
| `imgs-subdir` | `{article-dir}/imgs/` |
| `independent` (default) | `cover-image/{topic-slug}/` |
| Pasted content | `cover-image/{topic-slug}/` (always) |
```
cover-image/{topic-slug}/
<output-dir>/
├── source-{slug}.{ext} # Source files (text, images, etc.)
├── prompts/cover.md # Generation prompt
└── cover.png # Output image
```
**Slug Generation**:
1. Extract main topic from content (2-4 words, kebab-case)
2. Example: "The Future of AI" → `future-of-ai`
**Conflict Resolution**:
If `cover-image/{topic-slug}/` already exists:
- Append timestamp: `{topic-slug}-YYYYMMDD-HHMMSS`
- Example: `ai-future` exists → `ai-future-20260118-143052`
**Source Files**:
Copy all sources with naming `source-{slug}.{ext}`:
- `source-article.md`, `source-reference.png`, etc.
- Multiple sources supported: text, images, files from conversation
**Slug**: Extract main topic (2-4 words, kebab-case). Example: "The Future of AI" → `future-of-ai`
**Conflict**: If directory exists, append timestamp: `{topic-slug}-YYYYMMDD-HHMMSS`
**Source Files**: Copy all sources with naming `source-{slug}.{ext}` (multiple supported)
## Workflow
### Progress Checklist
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 1: Analyze content + determine output directory ⚠️ MUST ask if not configured
- [ ] Step 2: Confirm options (5 dimensions) ⚠️ REQUIRED unless --quick or all specified
- [ ] Step 3: Create prompt
- [ ] Step 4: Generate image
- [ ] Step 5: Completion report
@@ -184,7 +167,9 @@ Cover Image Progress:
### Flow
```
Input → [Step 0: Preferences/Setup] → Analyze → [Confirm: Type + Style + Aspect] → Prompt → Generate → Complete
Input → [Step 0: Preferences/Setup] → Analyze → [Output Dir ⚠️] → [Confirm: 5 Dimensions] → Prompt → Generate → Complete
(skip if --quick or all specified)
```
### Step 0: Load Preferences (EXTEND.md) ⚠️
@@ -201,249 +186,54 @@ test -f .baoyu-skills/baoyu-cover-image/EXTEND.md && echo "project"
test -f "$HOME/.baoyu-skills/baoyu-cover-image/EXTEND.md" && echo "user"
```
┌──────────────────────────────────────────────────┬───────────────────┐
│ Path │ Location │
├──────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-cover-image/EXTEND.md │ Project directory │
├──────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-cover-image/EXTEND.md │ User home │
└──────────────────────────────────────────────────┴───────────────────┘
| Result | Action |
|--------|--------|
| Found | Read, parse, display preferences summary → Continue to Step 1 |
| Not found | ⚠️ MUST run first-time setup ([references/config/first-time-setup.md](references/config/first-time-setup.md)) → Then continue to Step 1 |
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, display preferences summary (see below) → Continue to Step 1 │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ ⚠️ MUST run first-time setup (see below) → Then continue to Step 1 │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**Preferences Summary** (when EXTEND.md found):
Display loaded preferences:
**Preferences Summary** (when found):
```
Preferences loaded from [project/user]:
• Watermark: [enabled/disabled] [content if enabled]
• Type: [preferred_type or "auto"]
Style: [preferred_style or "auto"]
• Aspect: [default_aspect]
• Language: [language or "auto"]
• Type/Palette/Rendering: [value or "auto"]
Text: [value or "title-only"] | Mood: [value or "balanced"]
• Aspect: [default_aspect] | Output: [dir or "not set — will ask in Step 1.5"]
Quick mode: [enabled/disabled] | Language: [value or "auto"]
```
**First-Time Setup** (when EXTEND.md not found):
**EXTEND.md Supports**: Watermark | Preferred type | Preferred palette | Preferred rendering | Preferred text | Preferred mood | Default aspect ratio | Default output directory | Quick mode | Custom palette definitions | Language preference
**Language**: Use user's input language or saved language preference.
Use AskUserQuestion with ALL questions in ONE call:
**Q1: Watermark**
```yaml
header: "Watermark"
question: "Watermark text for generated cover images?"
options:
- label: "No watermark (Recommended)"
description: "Clean covers, can enable later in EXTEND.md"
```
**Q2: Preferred Type**
```yaml
header: "Type"
question: "Default cover type preference?"
options:
- label: "Auto-select (Recommended)"
description: "Choose based on content analysis each time"
- label: "hero"
description: "Large visual impact - product launch, announcements"
- label: "conceptual"
description: "Concept visualization - technical, architecture"
```
**Q3: Preferred Style**
```yaml
header: "Style"
question: "Default cover style preference?"
options:
- label: "Auto-select (Recommended)"
description: "Choose based on content analysis each time"
- label: "elegant"
description: "Refined, sophisticated - professional business"
- label: "notion"
description: "SaaS dashboard - productivity/tech content"
```
**Q4: Default Aspect Ratio**
```yaml
header: "Aspect"
question: "Default aspect ratio for cover images?"
options:
- label: "2.35:1 (Recommended)"
description: "Cinematic widescreen, best for article headers"
- label: "16:9"
description: "Standard widescreen, versatile"
- label: "1:1"
description: "Square, social media friendly"
```
**Q5: Save Location**
```yaml
header: "Save"
question: "Where to save preferences?"
options:
- label: "Project (Recommended)"
description: ".baoyu-skills/ (this project only)"
- label: "User"
description: "~/.baoyu-skills/ (all projects)"
```
**After setup**: Create EXTEND.md with user choices, then continue to Step 1.
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
Schema: `references/config/preferences-schema.md`
Schema: [references/config/preferences-schema.md](references/config/preferences-schema.md)
### Step 1: Analyze Content
Read source content, save it if needed, and perform 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. **Content analysis**:
- Extract: topic, core message, tone, keywords
- Identify visual metaphor opportunities
- Detect content type (technical/personal/business/creative)
4. **Language detection**:
- Detect source content language
- Note user's input language (from conversation)
- Compare with language preference in EXTEND.md
1. **Save source content** (if pasted, save to `source.md` in target directory; if file path, use as-is)
2. **Content analysis**: Extract topic, core message, tone, keywords; identify visual metaphors; detect content type
3. **Language detection**: Detect source language, note user's input language, compare with EXTEND.md preference
4. **Determine output directory** per File Structure rules. If no `default_output_dir` preference + file path input, include in Step 2 Q4
### Step 2: Confirm Options ⚠️
**Purpose**: Validate type, style and aspect ratio. **Do NOT skip.**
Validate all 5 dimensions + aspect ratio. Full confirmation flow: [references/workflow/confirm-options.md](references/workflow/confirm-options.md)
**Language**: Auto-determined (user's input language > saved preference > source language). No need to ask.
**Skip Conditions**:
Present options using AskUserQuestion:
**Question 1: Type** (if not specified via `--type`)
- Show recommended type based on content analysis + preferred type from EXTEND.md
```yaml
header: "Type"
question: "Which cover type?"
multiSelect: false
options:
- label: "[auto-recommended type] (Recommended)"
description: "[reason based on content signals]"
- label: "hero"
description: "Large visual impact, title overlay - product launch, announcements"
- label: "conceptual"
description: "Concept visualization - technical, architecture"
- label: "typography"
description: "Text-focused layout - opinions, quotes"
```
**Question 2: Style** (if not specified via `--style`)
- Based on selected Type, show compatible styles (✓✓ first from compatibility matrix)
- Format: `[style name] - [why it fits this content]`
```yaml
header: "Style"
question: "Which cover style?"
multiSelect: false
options:
- label: "[best compatible style] (Recommended)"
description: "[reason based on type + content]"
- label: "[style2]"
description: "[reason]"
- label: "[style3]"
description: "[reason]"
```
**Question 3: Aspect Ratio** (if not specified via `--aspect`)
```yaml
header: "Aspect"
question: "Cover aspect ratio?"
multiSelect: false
options:
- label: "2.35:1 (Recommended)"
description: "Cinematic widescreen, best for article headers"
- label: "16:9"
description: "Standard widescreen, versatile"
- label: "1:1"
description: "Square, social media friendly"
```
**After response**: Proceed to Step 3 with confirmed type + style + aspect ratio.
| Condition | Skipped | Still Asked |
|-----------|---------|-------------|
| `--quick` or `quick_mode: true` | 5 dimensions | Aspect ratio (unless `--aspect`) |
| All 5 + `--aspect` specified | All | None |
### Step 3: Create Prompt
Save to `prompts/cover.md`:
```markdown
Cover theme: [2-3 words]
Type: [confirmed type]
Style: [confirmed style]
Aspect ratio: [confirmed ratio]
Title text: [max 8 chars, or "none" if --no-title]
Language: [confirmed language]
Type composition:
- [Type-specific layout and structure]
Visual composition:
- Main visual: [type + style appropriate metaphor]
- Layout: [positioning based on type and aspect ratio]
- Decorative: [style elements]
Color scheme: [primary, background, accent from style]
Type notes: [key characteristics from type definition]
Style notes: [key characteristics from style definition]
[Watermark section if enabled]
```
**Type-Specific Composition**:
| Type | Composition Guidelines |
|------|------------------------|
| `hero` | Large focal visual (60-70% area), title overlay on visual, dramatic composition |
| `conceptual` | Abstract shapes representing core concepts, information hierarchy, clean zones |
| `typography` | Title as primary element (40%+ area), minimal supporting visuals, strong hierarchy |
| `metaphor` | Concrete object/scene representing abstract idea, symbolic elements, emotional resonance |
| `scene` | Atmospheric environment, narrative elements, mood-setting lighting and colors |
| `minimal` | Single focal element, generous whitespace (60%+), essential shapes only |
**Title guidelines** (if included):
- Max 8 characters, punchy headline
- Use hooks: numbers, questions, contrasts
- Match confirmed language
**Watermark Application** (if enabled in preferences):
Add to prompt:
```
Include a subtle watermark "[content]" positioned at [position]
with approximately [opacity*100]% visibility. The watermark should
be legible but not distracting from the main content.
```
Reference: `references/config/watermark-guide.md`
Save to `prompts/cover.md`. Full template: [references/workflow/prompt-template.md](references/workflow/prompt-template.md)
### Step 4: Generate Image
**Image Generation Skill Selection**:
1. Check available image generation skills
2. If multiple skills available, ask user preference
3. Call selected skill with:
- Prompt file path
- Output image path: `cover.png`
- Aspect ratio parameter
**On failure**: Auto-retry once before reporting error.
1. Backup existing `cover.png``cover-backup-YYYYMMDD-HHMMSS.png` (if regenerating)
2. Check available image generation skills; if multiple, ask user preference
3. Call selected skill with prompt file path, output path (`cover.png`), aspect ratio
4. On failure: auto-retry once before reporting error
### Step 5: Completion Report
@@ -451,48 +241,50 @@ Reference: `references/config/watermark-guide.md`
Cover Generated!
Topic: [topic]
Type: [type name]
Style: [style name]
Aspect: [ratio]
Title: [title or "visual only"]
Language: [lang]
Watermark: [enabled/disabled]
Type: [type] | Palette: [palette] | Rendering: [rendering]
Text: [text] | Mood: [mood] | Aspect: [ratio]
Title: [title text or "visual only"]
Language: [lang] | Watermark: [enabled/disabled]
Location: [directory path]
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 file FIRST** → Regenerate with same settings |
| **Change dimension** | Backup existing → Confirm new value → **Update prompt file FIRST** → Regenerate |
**IMPORTANT**: When regenerating, ALWAYS update the prompt file (`prompts/cover.md`) FIRST before regenerating. This ensures changes are documented and reproducible.
All modifications automatically backup 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) + Step 2 (options) - 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 alias for `--text none`
- `--style` presets are backward-compatible; explicit `--palette`/`--rendering` override preset values
## References
**Styles**: `references/styles/<name>.md` - Style definitions
**Config**:
- `references/config/preferences-schema.md` - EXTEND.md schema
- `references/config/first-time-setup.md` - First-time setup flow
- `references/config/watermark-guide.md` - Watermark configuration
## Extension Support
Custom configurations via EXTEND.md. See **Step 0** for paths and supported options.
**Dimensions**: [text.md](references/dimensions/text.md) | [mood.md](references/dimensions/mood.md)
**Palettes**: [references/palettes/](references/palettes/)
**Renderings**: [references/renderings/](references/renderings/)
**Auto-Selection**: [references/auto-selection.md](references/auto-selection.md)
**Style Presets**: [references/style-presets.md](references/style-presets.md)
**Compatibility**: [references/compatibility.md](references/compatibility.md)
**Types**: [references/types.md](references/types.md)
**Workflow**: [confirm-options.md](references/workflow/confirm-options.md) | [prompt-template.md](references/workflow/prompt-template.md)
**Config**: [preferences-schema.md](references/config/preferences-schema.md) | [first-time-setup.md](references/config/first-time-setup.md) | [watermark-guide.md](references/config/watermark-guide.md)
@@ -0,0 +1,60 @@
# Auto-Selection Rules
When a dimension is omitted, select based on content signals.
## Auto Type Selection
| Signals | Type |
|---------|------|
| Product, launch, announcement, release, reveal | `hero` |
| Architecture, framework, system, API, technical, model | `conceptual` |
| Quote, opinion, insight, thought, headline, statement | `typography` |
| Philosophy, growth, abstract, meaning, reflection | `metaphor` |
| Story, journey, travel, lifestyle, experience, narrative | `scene` |
| Zen, focus, essential, core, simple, pure | `minimal` |
## Auto Palette Selection
| Signals | Palette |
|---------|---------|
| Personal story, emotion, lifestyle, human | `warm` |
| Business, professional, thought leadership, luxury | `elegant` |
| Architecture, system, API, technical, code | `cool` |
| Entertainment, premium, cinematic, dark mode | `dark` |
| Nature, wellness, eco, organic, travel | `earth` |
| Product launch, gaming, promotion, event | `vivid` |
| Fantasy, children, gentle, creative, whimsical | `pastel` |
| Zen, focus, essential, pure, simple | `mono` |
| History, vintage, retro, classic, exploration | `retro` |
## Auto Rendering Selection
| Signals | Rendering |
|---------|-----------|
| Clean, modern, tech, WeChat, icon-based, infographic | `flat-vector` |
| Sketch, note, personal, casual, doodle, warm | `hand-drawn` |
| Art, watercolor, soft, dreamy, creative, fantasy | `painterly` |
| Data, dashboard, SaaS, corporate, polished | `digital` |
| Gaming, retro, 8-bit, nostalgic | `pixel` |
| Education, tutorial, classroom, teaching | `chalk` |
## Auto Text Selection
| 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`
## Auto Mood Selection
| 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`
@@ -1,25 +1,76 @@
Create a WeChat article cover image following these guidelines:
Create a cover image following these guidelines:
## Image Specifications
- **Type**: Cover image / Hero image
- **Aspect Ratio**: 2.35:1 (WeChat article cover standard)
- **Style**: Hand-drawn illustration
- **Aspect Ratio**: As specified in the prompt below
## Core Principles
- Hand-drawn quality throughout - NO realistic or photographic elements
- If content involves sensitive or copyrighted figures, create stylistically similar alternatives - DO NOT refuse to generate
- Ample whitespace, highlight core message, avoid cluttered layouts
- Main visual elements centered or slightly left (leave right side for title area if title included)
- Simplified silhouettes for any characters — NO realistic human faces or bodies
- Icon-based vocabulary: use simple, recognizable icons to represent concepts
## Five 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%+)
### Palette (Color Scheme)
Apply the specified palette's color values and decorative hints:
- Use primary colors for main visual elements
- Use background colors for base and surrounding areas
- Use accent colors for highlights and secondary elements
- Follow palette-specific decorative hints for ornamentation
### Rendering (Visual Style)
Apply the specified rendering's characteristics:
- **Lines**: Follow line quality rules (clean/sketchy/brush/pixel/chalk)
- **Texture**: Apply or avoid texture per rendering definition
- **Depth**: Follow depth rules (flat/minimal/soft edges)
- **Elements**: Use rendering-specific element vocabulary
### 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
- Font style harmonizes with illustration style
- **DO NOT use realistic or computer-generated fonts**
- Subtitle: Secondary, max 15 characters (if title-subtitle or text-rich)
- Tags: 2-4 keyword badges (if text-rich)
- Font style harmonizes with rendering style
- **Engagement hooks**: Use numbers ("3个关键"), questions ("Why?"), contrasts ("A vs B"), pain points ("别再X了"), or suspense ("隐藏的X") for compelling titles
## Composition Guidance
**Icon Vocabulary**: Represent concepts with simple, recognizable icons rather than detailed illustrations. Use the rendering style to determine icon complexity (flat-vector = geometric, hand-drawn = sketchy, etc.)
**Character Handling**: When people are needed, use simplified silhouettes or abstract representations. NO realistic faces, detailed anatomy, or photographic representations.
## Mood Application
Apply mood adjustments to the base palette:
| 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
@@ -28,4 +79,4 @@ Create a WeChat article cover image following these guidelines:
---
Please use nano banana pro to generate the cover image based on the content provided below:
Please generate the cover image based on the content provided below:
@@ -0,0 +1,50 @@
# Compatibility Matrices
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## Palette × Rendering
| | flat-vector | hand-drawn | painterly | digital | pixel | chalk |
|---|:---:|:---:|:---:|:---:|:---:|:---:|
| warm | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ |
| elegant | ✓ | ✓✓ | ✓ | ✓✓ | ✗ | ✗ |
| cool | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ |
| dark | ✓ | ✓ | ✓ | ✓✓ | ✓ | ✓✓ |
| earth | ✓ | ✓✓ | ✓✓ | ✓ | ✗ | ✗ |
| vivid | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| pastel | ✓✓ | ✓✓ | ✓✓ | ✓ | ✗ | ✗ |
| mono | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ |
| retro | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✗ |
## Type × Rendering
| | flat-vector | hand-drawn | painterly | digital | pixel | chalk |
|---|:---:|:---:|:---:|:---:|:---:|:---:|
| 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 | ✓✓ | ✓✓ | ✗ |
@@ -33,73 +33,115 @@ No EXTEND.md found
**Language**: Use user's input language or saved language preference.
Use single AskUserQuestion with multiple questions (AskUserQuestion auto-adds "Other" option):
Use AskUserQuestion with ALL questions in ONE call:
### Question 1: Watermark
```
```yaml
header: "Watermark"
question: "Watermark text for generated cover images? Type your watermark content (e.g., name, @handle)"
question: "Watermark text for generated cover images?"
options:
- label: "No watermark (Recommended)"
description: "No watermark, can enable later in EXTEND.md"
description: "Clean covers, can enable later in EXTEND.md"
```
Position defaults to bottom-right.
### Question 2: Preferred Type
```
```yaml
header: "Type"
question: "Default cover type preference?"
options:
- label: "None (Recommended)"
description: "Auto-select based on content analysis"
- label: "Auto-select (Recommended)"
description: "Choose based on content analysis each time"
- label: "hero"
description: "Large visual impact - product launch, announcements"
- label: "conceptual"
description: "Concept visualization - technical, architecture"
- label: "typography"
description: "Text-focused layout - opinions, quotes"
```
### Question 3: Preferred Style
### Question 3: Preferred Palette
```
header: "Style"
question: "Default cover style preference? Or type another style name"
```yaml
header: "Palette"
question: "Default color palette preference?"
options:
- label: "None (Recommended)"
description: "Auto-select based on content analysis"
- label: "Auto-select (Recommended)"
description: "Choose based on content analysis each time"
- label: "elegant"
description: "Refined, sophisticated - professional business"
- label: "blueprint"
description: "Technical schematics - architecture/system design"
- label: "notion"
description: "SaaS dashboard - productivity/tech content"
description: "Sophisticated - soft coral, muted teal, dusty rose"
- label: "warm"
description: "Friendly - orange, golden yellow, terracotta"
- label: "cool"
description: "Technical - engineering blue, navy, cyan"
```
### Question 4: Default Aspect Ratio
### Question 4: Preferred Rendering
```yaml
header: "Rendering"
question: "Default rendering style preference?"
options:
- label: "Auto-select (Recommended)"
description: "Choose based on content analysis each time"
- label: "hand-drawn"
description: "Sketchy organic illustration with personal touch"
- label: "flat-vector"
description: "Clean modern vector with geometric shapes"
- label: "digital"
description: "Polished precise digital illustration"
```
### Question 5: Default Aspect Ratio
```yaml
header: "Aspect"
question: "Default aspect ratio for cover images?"
options:
- label: "2.35:1 (Recommended)"
description: "Cinematic widescreen, best for article headers"
- label: "16:9"
description: "Standard widescreen, versatile"
- label: "16:9 (Recommended)"
description: "Standard widescreen - YouTube, presentations, versatile"
- label: "2.35:1"
description: "Cinematic widescreen - article headers, blog posts"
- label: "1:1"
description: "Square, social media friendly"
description: "Square - Instagram, WeChat, social cards"
- label: "3:4"
description: "Portrait - Xiaohongshu, Pinterest, mobile content"
```
### Question 5: Save Location
Note: More ratios (4:3, 3:2) available during generation. This sets the default recommendation.
### Question 6: Default Output Directory
```yaml
header: "Output"
question: "Default output directory for cover images?"
options:
- label: "Independent (Recommended)"
description: "cover-image/{topic-slug}/ - separate from article"
- label: "Same directory"
description: "{article-dir}/ - alongside the article file"
- label: "imgs subdirectory"
description: "{article-dir}/imgs/ - images folder near article"
```
### Question 7: 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"
```
### Question 8: Save Location
```yaml
header: "Save"
question: "Where to save preferences?"
options:
- label: "Project"
- label: "Project (Recommended)"
description: ".baoyu-skills/ (this project only)"
- label: "User"
description: "~/.baoyu-skills/ (all projects)"
@@ -123,17 +165,22 @@ options:
```yaml
---
version: 1
version: 3
watermark:
enabled: [true/false]
content: "[user input or empty]"
position: bottom-right
opacity: 0.7
preferred_type: [selected type or null]
preferred_style: [selected style or null]
default_aspect: [2.35:1/16:9/1:1]
preferred_palette: [selected palette or null]
preferred_rendering: [selected rendering or null]
preferred_text: title-only
preferred_mood: balanced
default_aspect: [16:9/2.35:1/1:1/3:4]
default_output_dir: [independent/same-dir/imgs-subdir]
quick_mode: [true/false]
language: null
custom_styles: []
custom_palettes: []
---
```
@@ -142,4 +189,6 @@ custom_styles: []
Users can edit EXTEND.md directly or run setup again:
- Delete EXTEND.md to trigger setup
- Edit YAML frontmatter for quick changes
- Full schema: `config/preferences-schema.md`
- Full schema: `preferences-schema.md`
**EXTEND.md Supports**: Watermark | Preferred type | Preferred palette | Preferred rendering | Preferred text | Preferred mood | Default aspect ratio | Default output directory | Quick mode | Custom palette definitions | Language preference
@@ -9,31 +9,37 @@ description: EXTEND.md YAML schema for baoyu-cover-image user preferences
```yaml
---
version: 1
version: 3
watermark:
enabled: false
content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
opacity: 0.7 # 0.1-1.0
preferred_type: null # hero|conceptual|typography|metaphor|scene|minimal or null for auto-select
preferred_style: null # Built-in style name or null for auto-select
preferred_palette: null # warm|elegant|cool|dark|earth|vivid|pastel|mono|retro or null for auto-select
preferred_rendering: null # flat-vector|hand-drawn|painterly|digital|pixel|chalk 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:
- name: my-style
description: "Style description"
color_palette:
custom_palettes:
- name: my-palette
description: "Palette description"
colors:
primary: ["#1E3A5F", "#4A90D9"]
background: "#F5F7FA"
accents: ["#00B4D8"]
visual_elements: "Clean lines, geometric shapes"
typography: "Modern sans-serif"
decorative_hints: "Clean lines, geometric shapes"
best_for: "Business, tech content"
---
```
@@ -42,16 +48,19 @@ custom_styles:
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `version` | int | 1 | Schema version |
| `version` | int | 3 | 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_palette` | string | null | Palette name or null for auto |
| `preferred_rendering` | string | null | Rendering 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 |
| `custom_palettes` | array | [] | User-defined palettes |
## Type Options
@@ -64,6 +73,48 @@ custom_styles:
| `scene` | Atmospheric scene, narrative feel |
| `minimal` | Minimalist composition, generous whitespace |
## Palette Options
| Value | Description |
|-------|-------------|
| `warm` | Friendly, approachable — orange, golden yellow, terracotta |
| `elegant` | Sophisticated, refined — soft coral, muted teal, dusty rose |
| `cool` | Technical, professional — engineering blue, navy, cyan |
| `dark` | Cinematic, premium — electric purple, cyan, magenta |
| `earth` | Natural, organic — forest green, sage, earth brown |
| `vivid` | Energetic, bold — bright red, neon green, electric blue |
| `pastel` | Gentle, whimsical — soft pink, mint, lavender |
| `mono` | Clean, focused — black, near-black, white |
| `retro` | Nostalgic, vintage — muted orange, dusty pink, maroon |
## Rendering Options
| Value | Description |
|-------|-------------|
| `flat-vector` | Clean outlines, uniform fills, geometric icons |
| `hand-drawn` | Sketchy, organic, imperfect strokes, paper texture |
| `painterly` | Soft brush strokes, color bleeds, watercolor feel |
| `digital` | Polished, precise edges, subtle gradients, UI components |
| `pixel` | Pixel grid, dithering, chunky 8-bit shapes |
| `chalk` | Chalk strokes, dust effects, blackboard texture |
## 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 |
@@ -81,29 +132,32 @@ custom_styles:
| `16:9` | Standard widescreen | Presentations, video thumbnails |
| `1:1` | Square | Social media, profile images |
## Custom Style Fields
## Custom Palette Fields
| Field | Required | Description |
|-------|----------|-------------|
| `name` | Yes | Unique style identifier (kebab-case) |
| `description` | Yes | What the style conveys |
| `color_palette.primary` | No | Main colors (array) |
| `color_palette.background` | No | Background color |
| `color_palette.accents` | No | Accent colors (array) |
| `visual_elements` | No | Decorative elements |
| `typography` | No | Font/lettering style |
| `name` | Yes | Unique palette identifier (kebab-case) |
| `description` | Yes | What the palette conveys |
| `colors.primary` | No | Main colors (array of hex) |
| `colors.background` | No | Background color (hex) |
| `colors.accents` | No | Accent colors (array of hex) |
| `decorative_hints` | No | Decorative elements and patterns |
| `best_for` | No | Recommended content types |
## Example: Minimal Preferences
```yaml
---
version: 1
version: 3
watermark:
enabled: true
content: "@myhandle"
preferred_type: null
preferred_style: elegant
preferred_palette: elegant
preferred_rendering: hand-drawn
preferred_text: title-only
preferred_mood: balanced
quick_mode: false
---
```
@@ -111,30 +165,97 @@ preferred_style: elegant
```yaml
---
version: 1
version: 3
watermark:
enabled: true
content: "myblog.com"
position: bottom-right
opacity: 0.5
preferred_type: conceptual
preferred_style: blueprint
preferred_palette: cool
preferred_rendering: digital
preferred_text: title-subtitle
preferred_mood: subtle
default_aspect: "16:9"
quick_mode: true
language: en
custom_styles:
custom_palettes:
- name: corporate-tech
description: "Professional B2B tech style"
color_palette:
description: "Professional B2B tech palette"
colors:
primary: ["#1E3A5F", "#4A90D9"]
background: "#F5F7FA"
accents: ["#00B4D8", "#48CAE4"]
visual_elements: "Clean lines, subtle gradients, circuit patterns"
typography: "Modern sans-serif, professional"
decorative_hints: "Clean lines, subtle gradients, circuit patterns"
best_for: "SaaS, enterprise, technical"
---
```
## Migration from v2
When loading v2 schema, auto-upgrade:
| v2 Field | v3 Field | Migration |
|----------|----------|-----------|
| `version: 2` | `version: 3` | Update |
| `preferred_style` | `preferred_palette` + `preferred_rendering` | Use preset mapping table |
| `custom_styles` | `custom_palettes` | Rename, restructure fields |
**Style → Palette + Rendering mapping**:
| v2 `preferred_style` | v3 `preferred_palette` | v3 `preferred_rendering` |
|----------------------|----------------------|-------------------------|
| `elegant` | `elegant` | `hand-drawn` |
| `blueprint` | `cool` | `digital` |
| `chalkboard` | `dark` | `chalk` |
| `dark-atmospheric` | `dark` | `digital` |
| `editorial-infographic` | `cool` | `digital` |
| `fantasy-animation` | `pastel` | `painterly` |
| `flat-doodle` | `pastel` | `flat-vector` |
| `intuition-machine` | `retro` | `digital` |
| `minimal` | `mono` | `flat-vector` |
| `nature` | `earth` | `hand-drawn` |
| `notion` | `mono` | `digital` |
| `pixel-art` | `vivid` | `pixel` |
| `playful` | `pastel` | `hand-drawn` |
| `retro` | `retro` | `digital` |
| `sketch-notes` | `warm` | `hand-drawn` |
| `vector-illustration` | `retro` | `flat-vector` |
| `vintage` | `retro` | `hand-drawn` |
| `warm` | `warm` | `hand-drawn` |
| `watercolor` | `earth` | `painterly` |
| null (auto) | null | null |
**Custom style migration**:
| v2 Field | v3 Field |
|----------|----------|
| `custom_styles[].name` | `custom_palettes[].name` |
| `custom_styles[].description` | `custom_palettes[].description` |
| `custom_styles[].color_palette` | `custom_palettes[].colors` |
| `custom_styles[].visual_elements` | `custom_palettes[].decorative_hints` |
| `custom_styles[].typography` | (removed — determined by rendering) |
| `custom_styles[].best_for` | `custom_palettes[].best_for` |
## Migration from v1
When loading v1 schema, auto-upgrade to v3:
| v1 Field | v3 Field | Default Value |
|----------|----------|---------------|
| (missing) | `version` | 3 |
| (missing) | `preferred_palette` | null |
| (missing) | `preferred_rendering` | null |
| (missing) | `preferred_text` | title-only |
| (missing) | `preferred_mood` | balanced |
| (missing) | `quick_mode` | false |
v1 `--no-title` flag maps to `preferred_text: none`.
@@ -28,15 +28,6 @@ description: Watermark configuration guide for baoyu-cover-image
| `bottom-center` | Centered designs | Text-heavy bottom area |
| `top-right` | Bottom-heavy content | Title/header in top-right |
## Opacity Examples
| Opacity | Visual Effect | Use Case |
|---------|---------------|----------|
| 0.3 | Very subtle, barely visible | Clean aesthetic priority |
| 0.5 | Balanced, noticeable but not distracting | Default recommendation |
| 0.7 | Clearly visible | Brand recognition priority |
| 0.9 | Strong presence | Anti-copy protection |
## Content Format
| Format | Example | Style |
@@ -51,16 +42,14 @@ description: Watermark configuration guide for baoyu-cover-image
1. **Consistency**: Use same watermark across all covers
2. **Legibility**: Ensure watermark readable on both light/dark areas
3. **Size**: Keep subtle - should not distract from content
4. **Contrast**: Adjust opacity based on cover background
## Prompt Integration
When watermark is enabled, add to 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 main content.
Include a subtle watermark "[content]" positioned at [position].
The watermark should be legible but not distracting from the main content.
```
## Cover-Specific Considerations
@@ -75,7 +64,6 @@ be legible but not distracting from the main content.
| Issue | Solution |
|-------|----------|
| Watermark invisible | Increase opacity or adjust position |
| Watermark too prominent | Decrease opacity (0.3-0.5) |
| Watermark invisible | Adjust position or check contrast |
| Watermark too prominent | Change position or reduce size |
| Watermark overlaps title | Change position or reduce title area |
| Inconsistent appearance | Use fixed opacity/position in preferences |
@@ -0,0 +1,139 @@
---
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
## Palette Interaction
Mood modifies the base palette characteristics:
| Palette Category | subtle | balanced | bold |
|------------------|--------|----------|------|
| Warm palettes (warm, earth, pastel) | More whitespace, softer tones | Standard colors | Deeper, richer warm tones |
| Cool palettes (cool, mono, elegant) | Lighter lines, muted colors | Standard colors | Stronger contrast, sharper definition |
| Dark palettes (dark, vivid) | Reduced contrast, softer glow | Standard colors | Maximum impact, vivid saturation |
| Vintage palettes (retro) | More faded, sepia-heavy | Standard colors | Bolder retro contrasts |
## Rendering Interaction
Mood adjusts rendering characteristics:
| Rendering | subtle | balanced | bold |
|-----------|--------|----------|------|
| flat-vector | Thinner strokes, lighter fills | Standard weight | Thicker strokes, stronger fills |
| hand-drawn | Lighter pencil pressure, more space | Standard strokes | Heavier marker strokes, denser elements |
| painterly | Diluted washes, more white | Standard brush | Thicker paint, saturated strokes |
| digital | Reduced shadows, lower contrast | Standard rendering | Stronger shadows, sharper edges |
| pixel | Fewer colors, simpler shapes | Standard palette | More colors, denser pixel detail |
| chalk | Lighter chalk, more board showing | Standard chalk | Heavy chalk, vivid colors, dense marks |
## 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,140 @@
---
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
- Match content language
- Use engagement hooks (see below)
**Engagement Hooks**:
| Hook Type | Examples (EN) | Examples (ZH) |
|-----------|---------------|---------------|
| Numbers | "3 Traps", "5 Keys" | "3个陷阱", "5个关键" |
| Questions | "Why X?", "How?" | "为什么X?", "怎么做?" |
| Contrasts | "A vs B", "Old→New" | "A还是B", "旧→新" |
| Pain Points | "Stop X", "Never Do" | "别再X了", "千万别" |
| Suspense | "Hidden X", "Secret" | "隐藏的X", "秘密" |
### 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
**Title Guidelines**:
- Use engagement hooks: numbers, questions, contrasts, pain points, suspense
- Punchy, action-oriented
**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
**Title Guidelines**:
- Use engagement hooks: numbers, questions, contrasts, pain points, suspense
- Punchy, action-oriented
**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`
@@ -0,0 +1,26 @@
# cool
Technical, professional, precise
## Color Palette
| Role | Color | Hex |
|------|-------|-----|
| Primary 1 | Engineering Blue | #2563EB |
| Primary 2 | Navy Blue | #1E3A5F |
| Primary 3 | Cyan | #06B6D4 |
| Background | Light Gray | #F8F9FA |
| Background Alt | Blueprint Off-White | #FAF8F5 |
| Accent 1 | Amber | #F59E0B |
| Accent 2 | Light Blue | #BFDBFE |
## Decorative Hints
- Grid lines and alignment guides
- Dimension indicators and measurements
- Technical schematics and diagrams
- Geometric precision elements
## Best For
Architecture, system design, API, technical documentation, engineering, data analysis
@@ -0,0 +1,26 @@
# dark
Cinematic, premium, atmospheric
## Color Palette
| Role | Color | Hex |
|------|-------|-----|
| Primary 1 | Electric Purple | #8B5CF6 |
| Primary 2 | Cyan Blue | #06B6D4 |
| Primary 3 | Magenta Pink | #EC4899 |
| Background | Deep Purple-Black | #0A0A0A |
| Background Alt | Rich Navy | #1A1A2E |
| Accent 1 | Amber | #F59E0B |
| Accent 2 | Pure White | #FFFFFF |
## Decorative Hints
- Glowing accent elements and neon highlights
- Atmospheric fog or particle effects
- Silhouettes with backlit edges
- Subtle gradient backgrounds
## Best For
Entertainment, premium brands, cinematic storytelling, dark mode, gaming, night themes
@@ -0,0 +1,26 @@
# earth
Natural, organic, grounded
## Color Palette
| Role | Color | Hex |
|------|-------|-----|
| Primary 1 | Forest Green | #276749 |
| Primary 2 | Sage | #9AE6B4 |
| Primary 3 | Earth Brown | #744210 |
| Background | Sand Beige | #F5E6D3 |
| Background Alt | Sky Blue | #E0F2FE |
| Accent 1 | Sunset Orange | #ED8936 |
| Accent 2 | Water Blue | #63B3ED |
## Decorative Hints
- Leaves, trees, mountains, natural forms
- Sun, clouds, organic flowing lines
- Botanical illustrations
- Earthy textures and natural patterns
## Best For
Nature, wellness, eco, organic, travel, sustainability, outdoor topics, slow living
@@ -0,0 +1,26 @@
# elegant
Sophisticated, refined, understated luxury
## Color Palette
| Role | Color | Hex |
|------|-------|-----|
| Primary 1 | Soft Coral | #E8A598 |
| Primary 2 | Muted Teal | #5B8A8A |
| Primary 3 | Dusty Rose | #D4A5A5 |
| Background | Warm Cream | #F5F0E6 |
| Background Alt | Soft Beige | #F0EBE0 |
| Accent 1 | Gold | #C9A962 |
| Accent 2 | Copper | #B87333 |
## Decorative Hints
- Delicate ornamental details
- Subtle gradients and soft transitions
- Refined geometric patterns
- Balanced, symmetrical compositions
## Best For
Business, professional, thought leadership, luxury, corporate communications
@@ -0,0 +1,26 @@
# mono
Clean, focused, essential
## Color Palette
| Role | Color | Hex |
|------|-------|-----|
| Primary 1 | Pure Black | #000000 |
| Primary 2 | Near Black | #1F1F1F |
| Primary 3 | Dark Gray | #374151 |
| Background | White | #FFFFFF |
| Background Alt | Off-White | #FAFAFA |
| Accent 1 | Content-derived single color | - |
| Accent 2 | Medium Gray | #9CA3AF |
## Decorative Hints
- Maximum negative space
- Thin lines and minimal strokes
- Single focal point emphasis
- Stark contrast between elements
## Best For
Zen, focus, essential concepts, pure, simple, minimalist philosophy, clean design
@@ -0,0 +1,26 @@
# pastel
Gentle, whimsical, soft
## Color Palette
| Role | Color | Hex |
|------|-------|-----|
| Primary 1 | Soft Pink | #FFB6C1 |
| Primary 2 | Mint | #98D8C8 |
| Primary 3 | Lavender | #C8A2C8 |
| Background | White | #FFFFFF |
| Background Alt | Light Cream | #FFF8E7 |
| Accent 1 | Butter Yellow | #FFFACD |
| Accent 2 | Sky Blue | #BEE3F8 |
## Decorative Hints
- Cute rounded proportions
- Stars, sparkles, flowers, decorative flourishes
- Soft shadows and gentle highlights
- Storybook-style elements
## Best For
Fantasy, children, gentle content, creative, whimsical, casual, beginner guides
@@ -0,0 +1,30 @@
# retro
Nostalgic, vintage, classic
## Color Palette
| Role | Color | Hex |
|------|-------|-----|
| Primary 1 | Coral Red | #E07A5F |
| Primary 2 | Mint Green | #81B29A |
| Primary 3 | Mustard Yellow | #F2CC8F |
| Primary 4 | Dark Maroon | #5D3A3A |
| Background | Cream Off-White | #F5F0E6 |
| Background Alt | Aged Paper | #F5E6D3 |
| Accent 1 | Burnt Orange | #D4764A |
| Accent 2 | Rock Blue | #577590 |
| Accent 3 | Vintage Gold | #C9A227 |
| Accent 4 | Faded Teal | #2F7373 |
## Decorative Hints
- Halftone dots and vintage badges
- Aged textures with subtle paper grain
- Sunburst/radiating lines for energy
- Pill-shaped clouds, small dots and stars
- Classic icons and retro motifs
## Best For
History, vintage, retro, classic, exploration, retrospectives, throwback content, creative proposals, educational
@@ -0,0 +1,26 @@
# vivid
Energetic, bold, attention-grabbing
## Color Palette
| Role | Color | Hex |
|------|-------|-----|
| Primary 1 | Bright Red | #EF4444 |
| Primary 2 | Neon Green | #22C55E |
| Primary 3 | Electric Blue | #3B82F6 |
| Background | Light Blue | #EFF6FF |
| Background Alt | Soft Lavender | #F5F3FF |
| Accent 1 | Bright Orange | #FB923C |
| Accent 2 | Vivid Yellow | #FACC15 |
## Decorative Hints
- Dynamic diagonal lines and angles
- Bold geometric shapes and color blocks
- Dramatic lighting effects
- High-energy visual compositions
## Best For
Product launch, gaming, promotion, event, marketing, announcements, brand showcases
@@ -0,0 +1,26 @@
# warm
Friendly, approachable, human-centered
## Color Palette
| Role | Color | Hex |
|------|-------|-----|
| Primary 1 | Warm Orange | #ED8936 |
| Primary 2 | Golden Yellow | #F6AD55 |
| Primary 3 | Terracotta | #C05621 |
| Background | Cream | #FFFAF0 |
| Background Alt | Soft Peach | #FED7AA |
| Accent 1 | Deep Brown | #744210 |
| Accent 2 | Soft Red | #E53E3E |
## Decorative Hints
- Sun rays, warm lighting effects
- Rounded shapes, organic curves
- Hearts, smiling faces, friendly icons
- Warm gradient overlays
## Best For
Personal growth, lifestyle, education, human stories, emotion, community
@@ -0,0 +1,43 @@
# chalk
Educational, authentic, classroom
## Core Characteristics
Chalk on blackboard aesthetic with imperfect strokes, dust effects, and authentic classroom feel. Nostalgic educational warmth.
## Lines
- Imperfect chalk strokes with variable pressure
- Visible chalk texture and grain
- Slightly wobbly, hand-drawn quality
- Thick strokes for emphasis, thin for details
## Texture
- Chalk dust effects around text and elements
- Board surface (dark, slightly worn)
- Eraser smudges and residue
- Grainy chalk quality on all elements
## Depth
- None: flat chalk drawings on board surface
- Layering through erasure and redrawing
- No shadows or perspective
## Element Vocabulary
- Chalk doodles: stars, arrows, underlines
- Mathematical formulas and diagrams
- Stick figures and simple icons
- Connection lines with chalk feel
- Checkmarks, circles, boxes for lists
- Wooden frame border optional
## Typography Approach
- Hand-drawn chalk lettering
- Imperfect baseline, authentic classroom feel
- White or bright colored chalk for emphasis
- Variable sizing for hierarchy
@@ -0,0 +1,42 @@
# digital
Polished, precise, modern
## Core Characteristics
Clean digital illustration with polished finish, precise edges, and subtle modern effects. Feels like a professional UI mockup or corporate illustration.
## Lines
- Clean, precise, computer-perfect edges
- Consistent stroke weights
- Sharp corners where appropriate
- Anti-aliased smooth rendering
## Texture
- Smooth surfaces with no visible texture
- Subtle gradients permitted (soft, controlled)
- Frosted glass and blur effects
- Clean shadows with consistent direction
## Depth
- Subtle gradients and soft drop shadows
- Layered card-based layouts
- Light 3D effects (subtle, not realistic)
- Material Design-inspired elevation
## Element Vocabulary
- Polished icons and UI components
- Data visualizations: charts, graphs, metrics
- Card layouts and structured grids
- Tag chips, progress bars, status indicators
- Clean geometric shapes
## Typography Approach
- System UI or modern sans-serif (Inter, SF Pro style)
- Clean, functional, high readability
- Structured hierarchy with consistent spacing
@@ -0,0 +1,42 @@
# flat-vector
Clean, modern, geometric illustration
## Core Characteristics
Flat design with clean outlines, uniform fills, and no texture or depth. Think modern app icons, infographic illustrations, and vector-based editorial art.
## Lines
- Clean outlines with uniform stroke weight
- Closed shapes (coloring-book style)
- Rounded line endings, avoid sharp corners
- Consistent stroke width throughout
## Texture
- None: smooth, flat color fills only
- No gradients, shadows, or noise
- Solid color blocks
## Depth
- Flat: no shadows, no perspective
- 2D layering with overlap for depth illusion
- Optional 2.5D isometric layering (front/back occlusion, no atmospheric perspective)
- No 3D effects or bevels
## Element Vocabulary
- Geometric icons and simple shapes
- Bold outlined objects with clean fills
- Geometric simplification: complex objects → basic shapes (trees → lollipop/triangle, buildings → rectangles)
- "Toy model" aesthetic: cute, rounded proportions
- Decorative: dots, lines, sunbursts, pill-shaped clouds, small stars
- Isolated elements on clean backgrounds
## Typography Approach
- Clean sans-serif or bold geometric lettering
- Strong readability, consistent weight
- Easily scalable at any size
@@ -0,0 +1,40 @@
# hand-drawn
Sketchy, organic, personal
## Core Characteristics
Hand-drawn illustration with visible imperfections, organic line quality, and personal touch. Feels like a skilled artist's sketchbook or whiteboard drawing.
## Lines
- Sketchy, organic, slightly imperfect strokes
- Variable line weight (thicker at pressure points)
- Wavy connectors and arrows
- Natural hand tremor visible
## Texture
- Paper grain and subtle surface texture
- Pencil/pen/marker texture on strokes
- Casual fills with visible brush direction
## Depth
- Minimal: light hand-drawn shadows or hatching
- No realistic depth or perspective
- Simple layering with overlap
## Element Vocabulary
- Doodles, organic shapes, hand-lettered labels
- Conceptual icons with sketchy quality
- Connection lines with hand-drawn wavy feel
- Stars, arrows, underlines, circles, checkmarks
- Stick figures and simple characters
## Typography Approach
- Hand-lettered or marker-style text
- Bouncy baselines, organic feel
- Variable sizes for emphasis hierarchy
@@ -0,0 +1,40 @@
# painterly
Soft, artistic, expressive
## Core Characteristics
Watercolor or paint-style illustration with visible brush strokes, color bleeds, and artistic texture. Feels like a hand-painted art piece.
## Lines
- Soft brush strokes with variable opacity
- No hard outlines; edges defined by color transitions
- Organic flowing strokes with natural blending
## Texture
- Visible paint or watercolor wash textures
- Color bleeds and wet-on-wet effects
- Paper texture showing through transparent areas
- Brush stroke patterns visible
## Depth
- Soft edges with natural color blending
- Atmospheric depth through color fading
- Layered washes creating depth illusion
## Element Vocabulary
- Watercolor washes as backgrounds
- Natural elements: leaves, flowers, organic forms
- Soft gradients and color transitions
- Splatter and drip effects as accents
- Botanical and environmental motifs
## Typography Approach
- Elegant brush script or handwritten style
- Organic letterforms with brush texture
- Integrated with paint environment
@@ -0,0 +1,42 @@
# pixel
Retro 8-bit, nostalgic, chunky
## Core Characteristics
Pixel art aesthetic with visible pixel grid, limited color palette, and nostalgic gaming feel. Emulates classic 8-bit and 16-bit era graphics.
## Lines
- Pixel grid alignment, no anti-aliasing
- Staircase edges on diagonals
- Single-pixel or double-pixel outlines
- Blocky, angular forms
## Texture
- Dithering patterns for gradients
- No smooth transitions
- Cross-hatching with pixel precision
- Limited 16-32 color palette per scene
## Depth
- None: flat pixel planes only
- Parallax layering (foreground/background)
- No perspective or 3D effects
## Element Vocabulary
- 8-bit sprites and chunky shapes
- Simple iconography: stars, hearts, arrows
- Text bubbles with pixel borders
- Progress bars with chunky segments
- Retro gaming UI elements
## Typography Approach
- Pixelated bitmap font style
- Chunky blocky letterforms
- Fixed-width or monospace feel
- All-caps for headers
@@ -0,0 +1,32 @@
# Style Presets
`--style X` expands to a palette + rendering combination. Users can override either dimension.
| --style | Palette | Rendering |
|---------|---------|-----------|
| `elegant` | `elegant` | `hand-drawn` |
| `blueprint` | `cool` | `digital` |
| `chalkboard` | `dark` | `chalk` |
| `dark-atmospheric` | `dark` | `digital` |
| `editorial-infographic` | `cool` | `digital` |
| `fantasy-animation` | `pastel` | `painterly` |
| `flat-doodle` | `pastel` | `flat-vector` |
| `intuition-machine` | `retro` | `digital` |
| `minimal` | `mono` | `flat-vector` |
| `nature` | `earth` | `hand-drawn` |
| `notion` | `mono` | `digital` |
| `pixel-art` | `vivid` | `pixel` |
| `playful` | `pastel` | `hand-drawn` |
| `retro` | `retro` | `digital` |
| `sketch-notes` | `warm` | `hand-drawn` |
| `vector-illustration` | `retro` | `flat-vector` |
| `vintage` | `retro` | `hand-drawn` |
| `warm` | `warm` | `hand-drawn` |
| `watercolor` | `earth` | `painterly` |
## Override Examples
- `--style blueprint --rendering hand-drawn` = cool palette with hand-drawn rendering
- `--style elegant --palette warm` = warm palette with hand-drawn rendering
Explicit `--palette`/`--rendering` flags always override preset values.
@@ -1,25 +0,0 @@
# blueprint
Precise technical blueprint style with engineering aesthetic
## Color Palette
- Primary: Engineering Blue (#2563EB), Navy Blue (#1E3A5F)
- Background: Blueprint Off-White (#FAF8F5), subtle grid overlay
- Accents: Amber (#F59E0B), Light Blue (#BFDBFE)
## Visual Elements
- Precise lines with consistent stroke weights
- Technical schematics and clean vector graphics
- Dimension lines and measurement indicators
- Grid alignment for all elements
- Geometric precision for all shapes
## Typography
- Clean sans-serif hand lettering, technical and authoritative
## Best For
Technical architecture, system design, data analysis, engineering documentation
@@ -1,26 +0,0 @@
# bold-editorial
High-impact magazine editorial with bold visual expression
## Color Palette
- Primary: Pure White (#FFFFFF), Electric Blue (#3B82F6), Bright Orange (#FB923C)
- Background: Deep Black (#0A0A0A), Deep Blue (#0F172A)
- Accents: Magenta (#EC4899), Neon Green (#22C55E), Violet (#8B5CF6)
## Visual Elements
- Strong typography as visual element itself
- Geometric shapes and bold color blocks
- Full-bleed solid color backgrounds
- Dynamic diagonal lines and angles
- Dramatic lighting effects on text
- Minimal decoration, maximum impact
## Typography
- Bold condensed typeface, oversized headlines, all-caps, tight letter-spacing
## Best For
Product launches, marketing, keynote speeches, brand showcases, investor pitches
@@ -1,61 +0,0 @@
# chalkboard
Black chalkboard background with colorful chalk drawing style
## Design Aesthetic
Classic classroom chalkboard aesthetic with hand-drawn chalk illustrations. Nostalgic educational feel with imperfect, sketchy lines that capture the warmth of traditional teaching. Colorful chalk creates visual hierarchy while maintaining the authentic chalkboard experience.
## Background
- Color: Chalkboard Black (#1A1A1A) or Dark Green-Black (#1C2B1C)
- Texture: Realistic chalkboard texture with subtle scratches, dust particles, and faint eraser marks
## Typography
Hand-drawn chalk lettering style with visible chalk texture. Imperfect baseline adds authenticity. White or bright colored chalk for emphasis.
## Color Palette
| Role | Color | Hex | Usage |
|------|-------|-----|-------|
| Background | Chalkboard Black | #1A1A1A | Primary background |
| Alt Background | Green-Black | #1C2B1C | Traditional green board |
| Primary Text | Chalk White | #F5F5F5 | Main text, outlines |
| Accent 1 | Chalk Yellow | #FFE566 | Highlights, emphasis |
| Accent 2 | Chalk Pink | #FF9999 | Secondary highlights |
| Accent 3 | Chalk Blue | #66B3FF | Diagrams, links |
| Accent 4 | Chalk Green | #90EE90 | Success, nature |
| Accent 5 | Chalk Orange | #FFB366 | Warnings, energy |
## Visual Elements
- Hand-drawn chalk illustrations with sketchy, imperfect lines
- Chalk dust effects around text and key elements
- Doodles: stars, arrows, underlines, circles, checkmarks
- Mathematical formulas and simple diagrams
- Eraser smudges and chalk residue textures
- Wooden frame border optional
- Stick figures and simple icons
- Connection lines with hand-drawn feel
## Style Rules
### Do
- Maintain authentic chalk texture on all elements
- Use imperfect, hand-drawn quality throughout
- Add subtle chalk dust and smudge effects
- Create visual hierarchy with color variety
- Include playful doodles and annotations
### Don't
- Use perfect geometric shapes
- Create clean digital-looking lines
- Add photorealistic elements
- Use gradients or glossy effects
## Best For
Educational content, tutorials, classroom themes, teaching materials, workshops, informal learning sessions, knowledge sharing
@@ -1,25 +0,0 @@
# dark-atmospheric
Dark moody aesthetic with glowing accent elements
## Color Palette
- Primary: Electric Purple (#8B5CF6), Cyan Blue (#06B6D4), Magenta Pink (#EC4899)
- Background: Deep Purple-Black (#0D0D1A), Rich Navy (#1A1A2E)
- Accents: Amber (#F59E0B), Pure White (#FFFFFF)
## Visual Elements
- Glowing accent elements and borders
- Subtle gradient backgrounds
- Atmospheric fog or particle effects
- Neon-style highlights on key elements
- Silhouettes with backlit edges
## Typography
- Elegant serif or refined sans-serif in light/white with subtle glow
## Best For
Entertainment, creative industries, premium brands, cinematic storytelling
@@ -1,26 +0,0 @@
# editorial-infographic
Modern magazine-style editorial with clear visual storytelling
## Color Palette
- Primary: Near Black (#1A1A1A), Editorial Blue (#2563EB)
- Background: Pure White (#FFFFFF), Light Gray (#F8F9FA)
- Accents: Coral (#F97316), Emerald (#10B981), Amber (#F59E0B)
## Visual Elements
- Clean flat illustrations (not photos)
- Structured multi-section layouts
- Callout boxes for key insights
- Icon-based data visualization
- Visual metaphors for abstract concepts
- Pull quotes and highlight boxes
## Typography
- Bold display serif or modern sans-serif, editorial sophistication
## Best For
Technology explainers, science communication, research summaries, thought leadership
@@ -1,23 +0,0 @@
# elegant
Refined, sophisticated, understated
## Color Palette
- Primary: Soft coral (#E8A598), muted teal (#5B8A8A), dusty rose (#D4A5A5)
- Background: Warm cream (#F5F0E6), soft beige
- Accents: Gold (#C9A962), copper
## Visual Elements
- Delicate lines, refined icons
- Subtle gradients
- Balanced composition
## Typography
- Elegant serif-style hand lettering
## Best For
Professional content, thought leadership, business topics
@@ -1,25 +0,0 @@
# fantasy-animation
Whimsical hand-drawn animation style inspired by Ghibli and Disney
## Color Palette
- Primary: Golden Yellow (#F4D03F), Rose Pink (#E8A0BF), Deep Forest (#2D5A3D)
- Background: Soft Sky Blue (#E8F4FC), Warm Cream (#FFF8E7)
- Accents: Sage Green (#87A96B), Sky Blue (#7EC8E3), Coral (#F08080)
## Visual Elements
- Central illustrated character (friendly, expressive)
- Magical floating objects (books, orbs, sparkles)
- Storybook-style environment backgrounds
- Soft shadows and gentle highlights
- Decorative elements: stars, sparkles, flowers, leaves
## Typography
- Whimsical serif or decorative hand-lettered style, organic feel
## Best For
Educational content, storytelling, creative workshops, fantasy/gaming content
@@ -1,27 +0,0 @@
# flat-doodle
Cute, simple doodle illustrations with bold outlines
## Color Palette
- Primary: Bright pastel pink (#FFB6C1), mint (#98D8C8), lavender (#C8A2C8), butter yellow (#FFFACD)
- Background: Clean white (#FFFFFF)
- Accents: Bold black outlines, soft coral, sky blue
## Visual Elements
- Bold black outlines around all shapes
- Simple flat shapes with no shading
- Cute rounded proportions
- Productivity-themed icons
- Isolated elements on white background
- Minimal decorative details
## Typography
- Simple, clean sans-serif or hand-lettering
- Bold and easily readable
## Best For
Productivity content, SaaS articles, workflow tutorials, app features, beginner guides, casual business
@@ -1,26 +0,0 @@
# intuition-machine
Technical briefing style with aged paper and bilingual labels
## Color Palette
- Primary: Dark Maroon (#5D3A3A), Teal (#2F7373)
- Background: Aged Cream (#F5F0E6), subtle paper texture with light creases
- Accents: Warm Brown (#8B7355), Maroon (#722F37), Deep Charcoal (#2D2D2D)
## Visual Elements
- Isometric 3D technical illustrations or flat 2D diagrams
- Bilingual callout labels (English + Chinese)
- Faded thematic background patterns
- Clean black outlines on all elements
- Split or triptych layouts
- Key quote box at bottom
## Typography
- Bold display font in dark maroon, ALL CAPS for titles, bilingual labels
## Best For
Technical explanations, academic presentations, bilingual audiences, knowledge docs
@@ -1,23 +0,0 @@
# minimal
Ultra-clean, zen-like, focused
## Color Palette
- Primary: Pure black (#000000), white (#FFFFFF)
- Background: White or off-white (#FAFAFA)
- Accents: Single color (user's choice or content-derived)
## Visual Elements
- Single focal point
- Maximum negative space
- Thin lines
## Typography
- Clean, simple hand lettering, lots of breathing room
## Best For
Philosophy, minimalism, focused concepts
@@ -1,22 +0,0 @@
# nature
Organic, calm, earthy
## Color Palette
- Primary: Forest green (#276749), sage (#9AE6B4), earth brown (#744210)
- Background: Sand beige (#F5E6D3), sky blue (#E0F2FE)
- Accents: Sunset orange, water blue
## Visual Elements
- Leaves, trees, mountains
- Sun, clouds, organic flowing lines
## Typography
- Organic, flowing hand lettering with natural textures
## Best For
Sustainability, wellness, outdoor topics, slow living
@@ -1,25 +0,0 @@
# notion
Clean SaaS dashboard aesthetic with productivity tool styling
## Color Palette
- Primary: Near Black (#1F1F1F), Notion Blue (#2383E2)
- Background: Light Gray (#F7F7F5), Pure White (#FFFFFF)
- Accents: Success Green (#0F7B6C), Alert Red (#E03E3E), Warning Yellow (#DFAB01)
## Visual Elements
- Card-based layouts with subtle borders
- Clean data visualizations
- Tag and label chips
- Icon-based navigation hints
- Progress bars and metric displays
## Typography
- System UI or Inter style, clean and functional
## Best For
Product demos, SaaS presentations, productivity content, B2B topics
@@ -1,26 +0,0 @@
# pixel-art
Retro 8-bit pixel art aesthetic with nostalgic gaming style
## Color Palette
- Primary: Dark Navy (#1A1A2E), Pixel Green (#00FF00)
- Background: Light Blue (#87CEEB), Soft Lavender (#E6E6FA)
- Accents: Pixel Red (#FF0000), Pixel Yellow (#FFFF00), Pixel Cyan (#00FFFF)
## Visual Elements
- All elements with visible pixel structure
- Simple iconography: stars, hearts, arrows
- Text bubbles with pixel borders
- Progress bars with chunky segments
- Dithering patterns for gradients
- Limited 16-32 color palette
## Typography
- Pixelated bitmap font style, chunky blocky letterforms
## Best For
Gaming content, tech tutorials, developer talks, retro-themed topics
@@ -1,23 +0,0 @@
# playful
Fun, creative, whimsical
## Color Palette
- Primary: Pastel pink (#FED7E2), mint (#C6F6D5), lavender (#E9D8FD), sky blue (#BEE3F8)
- Background: Light cream (#FFFBEB), soft white
- Accents: Bright pops - yellow, coral, turquoise
## Visual Elements
- Doodles, stars, swirls
- Cute characters, emoji-style icons
- Playful compositions
## Typography
- Bouncy, irregular hand lettering, playful angles
## Best For
Casual content, tutorials, beginner guides, fun topics
@@ -1,22 +0,0 @@
# retro
Vintage, nostalgic, classic
## Color Palette
- Primary: Muted orange (#ED8936 at 70%), dusty pink (#FED7E2 at 80%), faded teal
- Background: Aged paper (#F5E6D3), sepia tones
- Accents: Faded red, vintage gold
## Visual Elements
- Halftone dots, vintage badges
- Classic icons, aged textures
## Typography
- Vintage-style hand lettering, classic serif influence
## Best For
History, retrospectives, classic topics, throwback content
@@ -1,25 +0,0 @@
# sketch-notes
Soft hand-drawn illustration style with fresh minimalist aesthetic
## Color Palette
- Primary: Deep Charcoal (#2C3E50), Soft Orange (#F4A261)
- Background: Warm Off-White (#FAF8F0), subtle paper grain
- Accents: Mustard Yellow (#E9C46A), Sage Green (#87A96B), Light Blue (#7EC8E3)
## Visual Elements
- Connection lines with hand-drawn wavy feel
- Conceptual abstract icons illustrating ideas
- Color fills with hand-painted casual feel
- Doodle-style decorative elements
- Arrows and pointers with sketchy style
## Typography
- Bold hand-written marker font for headlines, casual handwriting for body
## Best For
Educational content, knowledge sharing, technical explanations, tutorials
@@ -1,25 +0,0 @@
# vector-illustration
Flat vector illustration with black outlines and retro soft colors
## Color Palette
- Primary: Coral Red (#E07A5F), Mint Green (#81B29A), Mustard Yellow (#F2CC8F)
- Background: Cream Off-White (#F5F0E6), subtle paper texture
- Accents: Burnt Orange (#D4764A), Rock Blue (#577590), Deep Charcoal (#2D2D2D) outlines
## Visual Elements
- All objects have closed black outlines (coloring book style)
- Rounded line endings, avoid sharp corners
- Simplified geometric shapes
- 2.5D perspective with layering
- Decorative elements: sunbursts, pill clouds, dots, stars
## Typography
- Large bold retro serif for titles, clean geometric sans-serif for body
## Best For
Educational content, creative proposals, brand showcases, explainer content
@@ -1,26 +0,0 @@
# vintage
Vintage aged-paper aesthetic with historical document styling
## Color Palette
- Primary: Dark Brown (#3D2914), Forest Green (#2D5A3D), Navy Blue (#1E3A5F)
- Background: Aged Parchment (#F5E6D3), Sepia Cream (#FFF8DC)
- Accents: Burgundy (#722F37), Gold (#C9A227), Sepia Black (#3D3D3D)
## Visual Elements
- Antique maps with route lines and landmarks
- Compass roses and nautical elements
- Specimen drawings (flora, fauna)
- Handwritten-style annotations
- Rope, leather, brass decorative motifs
- Aged texture with subtle creases
## Typography
- Classic serif with historical character, elegant flourishes
## Best For
Historical content, travel, heritage storytelling, biography, scientific discovery
@@ -1,22 +0,0 @@
# warm
Friendly, approachable, human-centered
## Color Palette
- Primary: Warm orange (#ED8936), golden yellow (#F6AD55), terracotta (#C05621)
- Background: Cream (#FFFAF0), soft peach (#FED7AA)
- Accents: Deep brown (#744210), soft red
## Visual Elements
- Rounded shapes, smiling faces
- Sun rays, hearts, warm lighting
## Typography
- Friendly rounded hand lettering
## Best For
Personal growth, lifestyle, education, human stories
@@ -1,25 +0,0 @@
# watercolor
Soft watercolor illustration style with hand-painted textures
## Color Palette
- Primary: Soft Coral (#F4A261), Dusty Rose (#E8A0A0)
- Background: Warm Off-White (#FAF8F0), Soft Cream (#FFF9E6)
- Accents: Sage Green (#87A96B), Sky Blue (#7EC8E3), Soft Lavender (#C5B4E3)
## Visual Elements
- Watercolor washes as backgrounds
- Visible brush strokes and textures
- Natural elements: leaves, flowers, organic shapes
- Color bleeds and soft edges
- Hand-drawn arrows and connections
## Typography
- Elegant handwritten or brush script, organic letterforms
## Best For
Lifestyle content, wellness, travel guides, personal stories, creative topics
@@ -0,0 +1,23 @@
# Type Composition Guidelines
## Type Gallery
| Type | Description | Best For |
|------|-------------|----------|
| `hero` | Large visual impact, title overlay | Product launch, brand promotion, major announcements |
| `conceptual` | Concept visualization, abstract core ideas | Technical articles, methodology, architecture design |
| `typography` | Text-focused layout, prominent title | Opinion pieces, quotes, insights |
| `metaphor` | Visual metaphor, concrete expressing abstract | Philosophy, growth, personal development |
| `scene` | Atmospheric scene, narrative feel | Stories, travel, lifestyle |
| `minimal` | Minimalist composition, generous whitespace | Zen, focus, core concepts |
## Type-Specific Composition
| Type | Composition Guidelines |
|------|------------------------|
| `hero` | Large focal visual (60-70% area), title overlay on visual, dramatic composition |
| `conceptual` | Abstract shapes representing core concepts, information hierarchy, clean zones |
| `typography` | Title as primary element (40%+ area), minimal supporting visuals, strong hierarchy |
| `metaphor` | Concrete object/scene representing abstract idea, symbolic elements, emotional resonance |
| `scene` | Atmospheric environment, narrative elements, mood-setting lighting and colors |
| `minimal` | Single focal element, generous whitespace (60%+), essential shapes only |
@@ -0,0 +1,132 @@
# Step 2: Confirm Options
## Purpose
Validate all 5 dimensions + aspect ratio.
## Skip Conditions
| Condition | Skipped Questions | Still Asked |
|-----------|-------------------|-------------|
| `--quick` flag | Type, Palette, Rendering, Text, Mood | **Aspect Ratio** (unless `--aspect` specified) |
| All 5 dimensions + `--aspect` specified | All | None |
| `quick_mode: true` in EXTEND.md | Type, Palette, Rendering, Text, Mood | **Aspect Ratio** (unless `--aspect` specified) |
| Otherwise | None | All 6 questions |
**Important**: Aspect ratio is ALWAYS asked unless explicitly specified via `--aspect` CLI flag. User presets in EXTEND.md are shown as recommended option, not auto-selected.
## Quick Mode Output
When skipping 5 dimensions:
```
Quick Mode: Auto-selected dimensions
• Type: [type] ([reason])
• Palette: [palette] ([reason])
• Rendering: [rendering] ([reason])
• Text: [text] ([reason])
• Mood: [mood] ([reason])
[Then ask Question 6: Aspect Ratio]
```
## Confirmation Flow
**Language**: Auto-determined (user's input language > saved preference > source language). No need to ask.
Present ALL options in a **single AskUserQuestion call** (4 questions max).
Skip any question where the dimension is already specified via CLI flag or `--style` preset.
### Q1: Type (skip if `--type`)
```yaml
header: "Type"
question: "Which cover type?"
multiSelect: false
options:
- label: "[auto-recommended type] (Recommended)"
description: "[reason based on content signals]"
- label: "hero"
description: "Large visual impact, title overlay - product launch, announcements"
- label: "conceptual"
description: "Concept visualization - technical, architecture"
- label: "typography"
description: "Text-focused layout - opinions, quotes"
```
### Q2: Palette (skip if `--palette` or `--style`)
```yaml
header: "Palette"
question: "Which color palette?"
multiSelect: false
options:
- label: "[auto-recommended palette] (Recommended)"
description: "[reason based on content signals]"
- label: "warm"
description: "Friendly - orange, golden yellow, terracotta"
- label: "elegant"
description: "Sophisticated - soft coral, muted teal, dusty rose"
- label: "cool"
description: "Technical - engineering blue, navy, cyan"
```
### Q3: Rendering (skip if `--rendering` or `--style`)
Show compatible renderings (✓✓ first from compatibility matrix):
```yaml
header: "Rendering"
question: "Which rendering style?"
multiSelect: false
options:
- label: "[best compatible rendering] (Recommended)"
description: "[reason based on palette + type + content]"
- label: "flat-vector"
description: "Clean outlines, flat fills, geometric icons"
- label: "hand-drawn"
description: "Sketchy, organic, imperfect strokes"
- label: "digital"
description: "Polished, precise, subtle gradients"
```
### Q4: Other Settings (skip if all remaining dimensions already specified)
Combine remaining settings into one question. Include: Output Dir (if no preference + file path input), Text, Mood, Aspect. Show auto-selected values as recommended option. User can accept all or type adjustments via "Other".
**When output dir needs asking** (no `default_output_dir` preference + file path input):
```yaml
header: "Settings"
question: "Output / Text / Mood / Aspect?"
multiSelect: false
options:
- label: "imgs/ / [auto-text] / [auto-mood] / [preset-aspect] (Recommended)"
description: "{article-dir}/imgs/, [text reason], [mood reason], [aspect source]"
- label: "same-dir / [auto-text] / [auto-mood] / [preset-aspect]"
description: "{article-dir}/, same directory as article"
- label: "independent / [auto-text] / [auto-mood] / [preset-aspect]"
description: "cover-image/{topic-slug}/, separate from article"
```
**When output dir already set** (preference exists or pasted content):
```yaml
header: "Settings"
question: "Text / Mood / Aspect?"
multiSelect: false
options:
- label: "[auto-text] / [auto-mood] / [preset-aspect] (Recommended)"
description: "Auto-selected: [text reason], [mood reason], [aspect source]"
- label: "[auto-text] / bold / [preset-aspect]"
description: "High contrast, vivid — matches [content signal]"
- label: "[auto-text] / subtle / [preset-aspect]"
description: "Low contrast, muted — calm, professional"
```
*Note*: "Other" (auto-added) allows typing custom combo. Parse `/`-separated values matching the question format.
## After Response
Proceed to Step 3 with confirmed dimensions.
@@ -0,0 +1,84 @@
# Step 3: Prompt Template
Save to `prompts/cover.md`:
```markdown
# 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]
Palette: [confirmed palette]
Rendering: [confirmed rendering]
Text level: [confirmed text level]
Mood: [confirmed mood]
Aspect ratio: [confirmed ratio]
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: [metaphor derived from content meaning]
- Layout: [positioning based on type and aspect ratio]
- Decorative: [palette-specific elements that reinforce content theme]
Color scheme: [primary, background, accent from palette definition, adjusted by mood]
Rendering notes: [key characteristics from rendering definition — lines, texture, depth, element style]
Type notes: [key characteristics from type definition]
Palette notes: [key characteristics from palette 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 |
|------|------------------------|
| `hero` | Large focal visual (60-70% area), title overlay on visual, dramatic composition |
| `conceptual` | Abstract shapes representing core concepts, information hierarchy, clean zones |
| `typography` | Title as primary element (40%+ area), minimal supporting visuals, strong hierarchy |
| `metaphor` | Concrete object/scene representing abstract idea, symbolic elements, emotional resonance |
| `scene` | Atmospheric environment, narrative elements, mood-setting lighting and colors |
| `minimal` | Single focal element, generous whitespace (60%+), essential shapes only |
## Title Guidelines
When text level includes title:
- Max 8 characters, punchy headline
- Use engagement hooks: numbers ("3个陷阱"), questions ("Why X?"), contrasts ("A vs B"), pain points ("别再X了")
- Match confirmed language
## Watermark Application
If enabled in preferences, add to prompt:
```
Include a subtle watermark "[content]" positioned at [position].
The watermark should be legible but not distracting from the main content.
```
Reference: `config/watermark-guide.md`
+47 -11
View File
@@ -1,11 +1,11 @@
---
name: baoyu-image-gen
description: Generates images using official OpenAI and Google APIs via AI SDK. Supports text-to-image, reference images, aspect ratios, and quality presets. Use when user asks to "generate image with API", "use official API for images", "create image with OpenAI/Google", or needs API-based generation instead of browser-based.
description: AI image generation with OpenAI, Google and DashScope APIs. Supports text-to-image, reference images, aspect ratios. Sequential by default; parallel generation available on request. Use when user asks to generate, create, or draw images.
---
# Image Generation (AI SDK)
Official API-based image generation. Supports OpenAI and Google providers.
Official API-based image generation. Supports OpenAI, Google and DashScope (阿里通义万象) providers.
## Script Directory
@@ -63,6 +63,9 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Make blue" --image out.png --r
# Specific provider
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image out.png --provider openai
# DashScope (阿里通义万象)
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "一只可爱的猫" --image out.png --provider dashscope
```
## Options
@@ -72,11 +75,12 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image out.png --provi
| `--prompt <text>`, `-p` | Prompt text |
| `--promptfiles <files...>` | Read prompt from files (concatenated) |
| `--image <path>` | Output image path (required) |
| `--provider google\|openai` | Force provider (default: google) |
| `--provider google\|openai\|dashscope` | Force provider (default: google) |
| `--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 |
@@ -87,10 +91,13 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image out.png --provi
|----------|-------------|
| `OPENAI_API_KEY` | OpenAI API key |
| `GOOGLE_API_KEY` | Google API key |
| `DASHSCOPE_API_KEY` | DashScope API key (阿里云) |
| `OPENAI_IMAGE_MODEL` | OpenAI model override |
| `GOOGLE_IMAGE_MODEL` | Google model override |
| `DASHSCOPE_IMAGE_MODEL` | DashScope model override (default: z-image-turbo) |
| `OPENAI_BASE_URL` | Custom OpenAI endpoint |
| `GOOGLE_BASE_URL` | Custom Google endpoint |
| `DASHSCOPE_BASE_URL` | Custom DashScope endpoint |
**Load Priority**: CLI args > env vars > `<cwd>/.baoyu-skills/.env` > `~/.baoyu-skills/.env`
@@ -98,21 +105,50 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image out.png --provi
1. `--provider` specified → use it
2. Only one API key available → use that provider
3. Both available → default to Google
3. Multiple available → default to Google
## 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
## Generation Mode
**Default**: Sequential generation (one image at a time). This ensures stable output and easier debugging.
**Parallel Generation**: Only use when user explicitly requests parallel/concurrent generation.
| Mode | When to Use |
|------|-------------|
| Sequential (default) | Normal usage, single images, small batches |
| Parallel | User explicitly requests, large batches (10+) |
**Parallel Settings** (when requested):
| Setting | Value |
|---------|-------|
| Recommended concurrency | 4 subagents |
| Max concurrency | 8 subagents |
| Use case | Large batch generation when user requests parallel |
**Agent Implementation** (parallel mode only):
```
# Launch multiple generations in parallel using Task tool
# Each Task runs as background subagent with run_in_background=true
# Collect results via TaskOutput when all complete
```
## Error Handling
+38 -259
View File
@@ -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:
@@ -40,11 +14,12 @@ Options:
-p, --prompt <text> Prompt text
--promptfiles <files...> Read prompt from files (concatenated)
--image <path> Output image path (required)
--provider google|openai Force provider (auto-detect by default)
--provider google|openai|dashscope Force provider (auto-detect by default)
-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,10 +28,14 @@ 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)
DASHSCOPE_API_KEY DashScope API key (阿里云通义万象)
OPENAI_IMAGE_MODEL Default OpenAI model (gpt-image-1.5)
GOOGLE_IMAGE_MODEL Default Google model (gemini-3-pro-image-preview)
DASHSCOPE_IMAGE_MODEL Default DashScope model (z-image-turbo)
OPENAI_BASE_URL Custom OpenAI endpoint
GOOGLE_BASE_URL Custom Google endpoint
DASHSCOPE_BASE_URL Custom DashScope endpoint
Env file load order: CLI args > process.env > <cwd>/.baoyu-skills/.env > ~/.baoyu-skills/.env`);
}
@@ -70,7 +49,8 @@ function parseArgs(argv: string[]): CliArgs {
model: null,
aspectRatio: null,
size: null,
quality: "normal",
quality: "2k",
imageSize: null,
referenceImages: [],
n: 1,
json: false,
@@ -128,7 +108,7 @@ function parseArgs(argv: string[]): CliArgs {
if (a === "--provider") {
const v = argv[++i];
if (v !== "google" && v !== "openai") throw new Error(`Invalid provider: ${v}`);
if (v !== "google" && v !== "openai" && v !== "dashscope") throw new Error(`Invalid provider: ${v}`);
out.provider = v;
continue;
}
@@ -161,6 +141,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,243 +244,34 @@ 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;
const hasDashscope = !!process.env.DASHSCOPE_API_KEY;
if (hasGoogle && !hasOpenai) return "google";
if (hasOpenai && !hasGoogle) return "openai";
if (hasGoogle && hasOpenai) return "google";
const available = [hasGoogle && "google", hasOpenai && "openai", hasDashscope && "dashscope"].filter(Boolean) as Provider[];
if (available.length === 1) return available[0]!;
if (available.length > 1) return available[0]!;
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, OPENAI_API_KEY, or DASHSCOPE_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 (provider === "dashscope") {
return (await import("./providers/dashscope")) as ProviderModule;
}
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 +303,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 +312,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,137 @@
import type { CliArgs } from "../types";
export function getDefaultModel(): string {
return process.env.DASHSCOPE_IMAGE_MODEL || "z-image-turbo";
}
function getApiKey(): string | null {
return process.env.DASHSCOPE_API_KEY || null;
}
function getBaseUrl(): string {
const base = process.env.DASHSCOPE_BASE_URL || "https://dashscope.aliyuncs.com";
return base.replace(/\/+$/g, "");
}
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 getSizeFromAspectRatio(ar: string | null, quality: CliArgs["quality"]): string {
const baseSize = quality === "2k" ? 1440 : 1024;
if (!ar) return `${baseSize}*${baseSize}`;
const parsed = parseAspectRatio(ar);
if (!parsed) return `${baseSize}*${baseSize}`;
const ratio = parsed.width / parsed.height;
if (Math.abs(ratio - 1) < 0.1) {
return `${baseSize}*${baseSize}`;
}
if (ratio > 1) {
const w = Math.round(baseSize * ratio);
return `${w}*${baseSize}`;
}
const h = Math.round(baseSize / ratio);
return `${baseSize}*${h}`;
}
function normalizeSize(size: string): string {
return size.replace("x", "*");
}
export async function generateImage(
prompt: string,
model: string,
args: CliArgs
): Promise<Uint8Array> {
const apiKey = getApiKey();
if (!apiKey) throw new Error("DASHSCOPE_API_KEY is required");
if (args.referenceImages.length > 0) {
console.error("Warning: Reference images not yet supported with DashScope, ignoring.");
}
const size = args.size ? normalizeSize(args.size) : getSizeFromAspectRatio(args.aspectRatio, args.quality);
const url = `${getBaseUrl()}/api/v1/services/aigc/multimodal-generation/generation`;
const body = {
model,
input: {
messages: [
{
role: "user",
content: [{ text: prompt }],
},
],
},
parameters: {
prompt_extend: false,
size,
},
};
console.log(`Generating image with DashScope (${model})...`, { size });
const res = await fetch(url, {
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(`DashScope API error (${res.status}): ${err}`);
}
const result = await res.json() as {
output?: {
result_image?: string;
choices?: Array<{
message?: {
content?: Array<{ image?: string }>;
};
}>;
};
};
let imageData: string | null = null;
if (result.output?.result_image) {
imageData = result.output.result_image;
} else if (result.output?.choices?.[0]?.message?.content) {
const content = result.output.choices[0].message.content;
for (const item of content) {
if (item.image) {
imageData = item.image;
break;
}
}
}
if (!imageData) {
console.error("Response:", JSON.stringify(result, null, 2));
throw new Error("No image in response");
}
if (imageData.startsWith("http://") || imageData.startsWith("https://")) {
const imgRes = await fetch(imageData);
if (!imgRes.ok) throw new Error("Failed to download image");
const buf = await imgRes.arrayBuffer();
return new Uint8Array(buf);
}
return Uint8Array.from(Buffer.from(imageData, "base64"));
}
@@ -0,0 +1,229 @@
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", "gemini-3-flash-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 normalizeGoogleModelId(model: string): string {
return model.startsWith("models/") ? model.slice("models/".length) : model;
}
function isGoogleMultimodal(model: string): boolean {
const normalized = normalizeGoogleModelId(model);
return GOOGLE_MULTIMODAL_MODELS.some((m) => normalized.includes(m));
}
function isGoogleImagen(model: string): boolean {
const normalized = normalizeGoogleModelId(model);
return GOOGLE_IMAGEN_MODELS.some((m) => normalized.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 getGoogleBaseUrl(): string {
const base = process.env.GOOGLE_BASE_URL || "https://generativelanguage.googleapis.com";
return base.replace(/\/+$/g, "");
}
function buildGoogleUrl(pathname: string): string {
const base = getGoogleBaseUrl();
const cleanedPath = pathname.replace(/^\/+/g, "");
if (base.endsWith("/v1beta")) return `${base}/${cleanedPath}`;
return `${base}/v1beta/${cleanedPath}`;
}
function toModelPath(model: string): string {
const modelId = normalizeGoogleModelId(model);
return `models/${modelId}`;
}
async function postGoogleJson<T>(pathname: string, body: unknown): Promise<T> {
const apiKey = getGoogleApiKey();
if (!apiKey) throw new Error("GOOGLE_API_KEY or GEMINI_API_KEY is required");
const res = await fetch(buildGoogleUrl(pathname), {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-goog-api-key": apiKey,
},
body: JSON.stringify(body),
});
if (!res.ok) {
const err = await res.text();
throw new Error(`Google API error (${res.status}): ${err}`);
}
return (await res.json()) as T;
}
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;
}
function addAspectRatioToPrompt(prompt: string, ar: string | null): string {
if (!ar) return prompt;
return `${prompt} Aspect ratio: ${ar}.`;
}
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 };
}
function extractInlineImageData(response: {
candidates?: Array<{ content?: { parts?: Array<{ inlineData?: { data?: string } }> } }>;
}): string | null {
for (const candidate of response.candidates || []) {
for (const part of candidate.content?.parts || []) {
const data = part.inlineData?.data;
if (typeof data === "string" && data.length > 0) return data;
}
}
return null;
}
function extractPredictedImageData(response: {
predictions?: Array<any>;
generatedImages?: Array<any>;
}): string | null {
const candidates = [...(response.predictions || []), ...(response.generatedImages || [])];
for (const candidate of candidates) {
if (!candidate || typeof candidate !== "object") continue;
if (typeof candidate.imageBytes === "string") return candidate.imageBytes;
if (typeof candidate.bytesBase64Encoded === "string") return candidate.bytesBase64Encoded;
if (typeof candidate.data === "string") return candidate.data;
const image = candidate.image;
if (image && typeof image === "object") {
if (typeof image.imageBytes === "string") return image.imageBytes;
if (typeof image.bytesBase64Encoded === "string") return image.bytesBase64Encoded;
if (typeof image.data === "string") return image.data;
}
}
return null;
}
async function generateWithGemini(
prompt: string,
model: string,
args: CliArgs
): Promise<Uint8Array> {
const promptWithAspect = addAspectRatioToPrompt(prompt, args.aspectRatio);
const parts: Array<{ text?: string; inlineData?: { data: string; mimeType: string } }> = [];
for (const refPath of args.referenceImages) {
const { data, mimeType } = await readImageAsBase64(refPath);
parts.push({ inlineData: { data, mimeType } });
}
parts.push({ text: promptWithAspect });
const imageConfig: { imageSize: "1K" | "2K" | "4K" } = {
imageSize: getGoogleImageSize(args),
};
console.log("Generating image with Gemini...", imageConfig);
const response = await postGoogleJson<{
candidates?: Array<{ content?: { parts?: Array<{ inlineData?: { data?: string } }> } }>;
}>(`${toModelPath(model)}:generateContent`, {
contents: [
{
role: "user",
parts,
},
],
generationConfig: {
responseModalities: ["IMAGE"],
imageConfig,
},
});
console.log("Generation completed.");
const imageData = extractInlineImageData(response);
if (imageData) return Uint8Array.from(Buffer.from(imageData, "base64"));
throw new Error("No image in response");
}
async function generateWithImagen(
prompt: string,
model: string,
args: CliArgs
): Promise<Uint8Array> {
const fullPrompt = buildPromptWithAspect(prompt, args.aspectRatio, args.quality);
const imageSize = getGoogleImageSize(args);
if (imageSize === "4K") {
console.error("Warning: Imagen models do not support 4K imageSize, using 2K instead.");
}
const parameters: Record<string, unknown> = {
sampleCount: args.n,
};
if (args.aspectRatio) {
parameters.aspectRatio = args.aspectRatio;
}
if (imageSize === "1K" || imageSize === "2K") {
parameters.imageSize = imageSize;
} else {
parameters.imageSize = "2K";
}
const response = await postGoogleJson<{
predictions?: Array<any>;
generatedImages?: Array<any>;
}>(`${toModelPath(model)}:predict`, {
instances: [
{
prompt: fullPrompt,
},
],
parameters,
});
const imageData = extractPredictedImageData(response);
if (imageData) return Uint8Array.from(Buffer.from(imageData, "base64"));
throw new Error("No image in response");
}
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");
}
+18
View File
@@ -0,0 +1,18 @@
export type Provider = "google" | "openai" | "dashscope";
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;
};
+18 -1
View File
@@ -56,8 +56,25 @@ npx -y bun ${SKILL_DIR}/scripts/wechat-browser.ts --title "标题" --content "
### Article (文章)
Before posting, ask user to choose a theme using AskUserQuestion:
| Theme | Description |
|-------|-------------|
| `default` | 经典主题 - 传统排版,标题居中带底边,二级标题白字彩底 |
| `grace` | 优雅主题 - 文字阴影,圆角卡片,精致引用块 (by @brzhang) |
| `simple` | 简洁主题 - 现代极简风,不对称圆角,清爽留白 (by @okooo5km) |
Default: `default`. If user has already specified a theme, skip the question.
**Workflow**:
1. Generate HTML preview and print the full `htmlPath` from JSON output so user can click to preview:
```bash
npx -y bun ${SKILL_DIR}/scripts/wechat-article.ts --markdown article.md --theme grace
npx -y bun ${SKILL_DIR}/scripts/md-to-wechat.ts article.md --theme <chosen-theme>
```
2. Post to WeChat:
```bash
npx -y bun ${SKILL_DIR}/scripts/wechat-article.ts --markdown article.md --theme <chosen-theme>
```
## Detailed References
@@ -53,7 +53,7 @@ Regular paragraph with **bold** and *italic*.
## Image Handling
1. **Parse**: Images in markdown are replaced with `[[IMAGE_PLACEHOLDER_N]]`
1. **Parse**: Images in markdown are replaced with `WECHATIMGPH_N`
2. **Render**: HTML is generated with placeholders in text
3. **Paste**: HTML content is pasted into WeChat editor
4. **Replace**: For each placeholder:
@@ -81,7 +81,7 @@ Claude:
3. Opens Chrome, navigates to WeChat editor
4. Pastes HTML content
5. For each image:
- Selects [[IMAGE_PLACEHOLDER_1]]
- Selects WECHATIMGPH_1
- Scrolls into view
- Presses Backspace to delete
- Pastes image
@@ -171,6 +171,37 @@ export interface ChromeSession {
targetId: string;
}
export async function tryConnectExisting(port: number): Promise<CdpConnection | null> {
try {
const version = await fetchJson<{ webSocketDebuggerUrl?: string }>(`http://127.0.0.1:${port}/json/version`);
if (version.webSocketDebuggerUrl) {
const cdp = await CdpConnection.connect(version.webSocketDebuggerUrl, 5_000);
return cdp;
}
} catch {}
return null;
}
export async function findExistingChromeDebugPort(): Promise<number | null> {
if (process.platform !== 'darwin' && process.platform !== 'linux') return null;
try {
const { execSync } = await import('node:child_process');
const cmd = process.platform === 'darwin'
? `lsof -nP -iTCP -sTCP:LISTEN 2>/dev/null | grep -i 'google\\|chrome' | awk '{print $9}' | sed 's/.*://'`
: `ss -tlnp 2>/dev/null | grep -i chrome | awk '{print $4}' | sed 's/.*://'`;
const output = execSync(cmd, { encoding: 'utf-8', timeout: 5_000 }).trim();
if (!output) return null;
const ports = output.split('\n').map(p => parseInt(p, 10)).filter(p => !isNaN(p) && p > 0);
for (const port of ports) {
try {
const version = await fetchJson<{ webSocketDebuggerUrl?: string }>(`http://127.0.0.1:${port}/json/version`);
if (version.webSocketDebuggerUrl) return port;
} catch {}
}
} catch {}
return null;
}
export async function launchChrome(url: string, profileDir?: string): Promise<{ cdp: CdpConnection; chrome: ReturnType<typeof spawn> }> {
const chromePath = findChromeExecutable();
if (!chromePath) throw new Error('Chrome not found. Set WECHAT_BROWSER_CHROME_PATH env var.');
@@ -3,6 +3,7 @@ import path from 'node:path';
import { mkdir, writeFile } from 'node:fs/promises';
import os from 'node:os';
import { createHash } from 'node:crypto';
import { fileURLToPath } from 'node:url';
import https from 'node:https';
import http from 'node:http';
import { spawnSync } from 'node:child_process';
@@ -111,27 +112,27 @@ function parseFrontmatter(content: string): { frontmatter: Record<string, string
return { frontmatter, body: match[2]! };
}
async function parseMarkdownForWechat(
markdownPath: string,
options?: { title?: string; theme?: string; tempDir?: string },
): Promise<ParsedResult> {
const content = fs.readFileSync(markdownPath, 'utf-8');
export async function convertMarkdown(markdownPath: string, options?: { title?: string; theme?: string }): Promise<ParsedResult> {
const baseDir = path.dirname(markdownPath);
const tempDir = options?.tempDir ?? path.join(os.tmpdir(), 'wechat-article-images');
const content = fs.readFileSync(markdownPath, 'utf-8');
const theme = options?.theme ?? 'default';
await mkdir(tempDir, { recursive: true });
const { frontmatter, body } = parseFrontmatter(content);
let title = options?.title ?? frontmatter.title ?? '';
if (!title) {
const h1Match = body.match(/^#\s+(.+)$/m);
if (h1Match) title = h1Match[1]!;
const lines = body.split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
const headingMatch = trimmed.match(/^#{1,2}\s+(.+)$/);
if (headingMatch) title = headingMatch[1]!;
break;
}
}
const author = frontmatter.author ?? '';
let summary = frontmatter.summary ?? frontmatter.description ?? '';
if (!title) title = path.basename(markdownPath, path.extname(markdownPath));
const author = frontmatter.author || '';
let summary = frontmatter.description || frontmatter.summary || '';
if (!summary) {
const lines = body.split('\n');
@@ -161,20 +162,23 @@ async function parseMarkdownForWechat(
let imageCounter = 0;
const modifiedBody = body.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, src) => {
const placeholder = `[[IMAGE_PLACEHOLDER_${++imageCounter}]]`;
const placeholder = `WECHATIMGPH_${++imageCounter}`;
images.push({ src, placeholder });
return placeholder;
});
const modifiedMarkdown = `---\n${Object.entries(frontmatter).map(([k, v]) => `${k}: ${v}`).join('\n')}\n---\n${modifiedBody}`;
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-article-images-'));
const tempMdPath = path.join(tempDir, 'temp-article.md');
await writeFile(tempMdPath, modifiedMarkdown, 'utf-8');
const scriptDir = path.dirname(new URL(import.meta.url).pathname);
const renderScript = path.join(scriptDir, 'md', 'render.ts');
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const renderScript = path.join(__dirname, 'md', 'render.ts');
console.error(`[md-to-wechat] Rendering markdown with theme: ${theme}`);
const result = spawnSync('npx', ['-y', 'bun', renderScript, tempMdPath, '--theme', theme], {
stdio: ['inherit', 'pipe', 'pipe'],
cwd: baseDir,
@@ -226,8 +230,8 @@ Output JSON format:
"htmlPath": "/tmp/wechat-article-images/temp-article.html",
"contentImages": [
{
"placeholder": "[[IMAGE_PLACEHOLDER_1]]",
"localPath": "/tmp/wechat-article-images/img.png",
"placeholder": "WECHATIMGPH_1",
"localPath": "/tmp/wechat-image/img.png",
"originalPath": "imgs/image.png"
}
]
@@ -262,7 +266,7 @@ async function main(): Promise<void> {
}
if (!markdownPath) {
console.error('Error: Markdown file path required');
console.error('Error: Markdown file path is required');
process.exit(1);
}
@@ -271,7 +275,7 @@ async function main(): Promise<void> {
process.exit(1);
}
const result = await parseMarkdownForWechat(markdownPath, { title, theme });
const result = await convertMarkdown(markdownPath, { title, theme });
console.log(JSON.stringify(result, null, 2));
}
@@ -2,7 +2,8 @@ import fs from 'node:fs';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import process from 'node:process';
import { launchChrome, getPageSession, waitForNewTab, clickElement, typeText, evaluate, sleep, type ChromeSession, type CdpConnection } from './cdp.ts';
import { fileURLToPath } from 'node:url';
import { launchChrome, tryConnectExisting, findExistingChromeDebugPort, getPageSession, waitForNewTab, clickElement, typeText, evaluate, sleep, type ChromeSession, type CdpConnection } from './cdp.ts';
const WECHAT_URL = 'https://mp.weixin.qq.com/';
@@ -24,6 +25,7 @@ interface ArticleOptions {
contentImages?: ImageInfo[];
submit?: boolean;
profileDir?: string;
cdpPort?: number;
}
async function waitForLogin(session: ChromeSession, timeoutMs = 120_000): Promise<boolean> {
@@ -36,6 +38,16 @@ async function waitForLogin(session: ChromeSession, timeoutMs = 120_000): Promis
return false;
}
async function waitForElement(session: ChromeSession, selector: string, timeoutMs = 10_000): Promise<boolean> {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const found = await evaluate<boolean>(session, `!!document.querySelector('${selector}')`);
if (found) return true;
await sleep(500);
}
return false;
}
async function clickMenuByText(session: ChromeSession, text: string): Promise<void> {
console.log(`[wechat] Clicking "${text}" menu...`);
const posResult = await session.cdp.send<{ result: { value: string } }>('Runtime.evaluate', {
@@ -65,8 +77,9 @@ async function clickMenuByText(session: ChromeSession, text: string): Promise<vo
}
async function copyImageToClipboard(imagePath: string): Promise<void> {
const scriptDir = path.dirname(new URL(import.meta.url).pathname);
const copyScript = path.join(scriptDir, './copy-to-clipboard.ts');
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const copyScript = path.join(__dirname, './copy-to-clipboard.ts');
const result = spawnSync('npx', ['-y', 'bun', copyScript, 'image', imagePath], { stdio: 'inherit' });
if (result.status !== 0) throw new Error(`Failed to copy image: ${imagePath}`);
}
@@ -78,6 +91,30 @@ async function pasteInEditor(session: ChromeSession): Promise<void> {
await session.cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'v', code: 'KeyV', modifiers, windowsVirtualKeyCode: 86 }, { sessionId: session.sessionId });
}
async function sendCopy(cdp?: CdpConnection, sessionId?: string): Promise<void> {
if (process.platform === 'darwin') {
spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "c" using command down']);
} else if (process.platform === 'linux') {
spawnSync('xdotool', ['key', 'ctrl+c']);
} else if (cdp && sessionId) {
await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'c', code: 'KeyC', modifiers: 2, windowsVirtualKeyCode: 67 }, { sessionId });
await sleep(50);
await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'c', code: 'KeyC', modifiers: 2, windowsVirtualKeyCode: 67 }, { sessionId });
}
}
async function sendPaste(cdp?: CdpConnection, sessionId?: string): Promise<void> {
if (process.platform === 'darwin') {
spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "v" using command down']);
} else if (process.platform === 'linux') {
spawnSync('xdotool', ['key', 'ctrl+v']);
} else if (cdp && sessionId) {
await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'v', code: 'KeyV', modifiers: 2, windowsVirtualKeyCode: 86 }, { sessionId });
await sleep(50);
await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'v', code: 'KeyV', modifiers: 2, windowsVirtualKeyCode: 86 }, { sessionId });
}
}
async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Promise<void> {
const absolutePath = path.isAbsolute(htmlFilePath) ? htmlFilePath : path.resolve(process.cwd(), htmlFilePath);
const fileUrl = `file://${absolutePath}`;
@@ -108,30 +145,24 @@ async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Pr
}, { sessionId });
await sleep(300);
console.log('[wechat] Copying with system Cmd+C...');
if (process.platform === 'darwin') {
spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "c" using command down']);
} else {
spawnSync('xdotool', ['key', 'ctrl+c']);
}
console.log('[wechat] Copying content...');
await sendCopy(cdp, sessionId);
await sleep(1000);
console.log('[wechat] Closing HTML tab...');
await cdp.send('Target.closeTarget', { targetId });
}
async function pasteFromClipboardInEditor(): Promise<void> {
if (process.platform === 'darwin') {
spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "v" using command down']);
} else {
spawnSync('xdotool', ['key', 'ctrl+v']);
}
async function pasteFromClipboardInEditor(session: ChromeSession): Promise<void> {
console.log('[wechat] Pasting content...');
await sendPaste(session.cdp, session.sessionId);
await sleep(1000);
}
async function parseMarkdownWithPlaceholders(markdownPath: string, theme?: string): Promise<{ title: string; author: string; summary: string; htmlPath: string; contentImages: ImageInfo[] }> {
const scriptDir = path.dirname(new URL(import.meta.url).pathname);
const mdToWechatScript = path.join(scriptDir, 'md-to-wechat.ts');
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const mdToWechatScript = path.join(__dirname, 'md-to-wechat.ts');
const args = ['-y', 'bun', mdToWechatScript, markdownPath];
if (theme) args.push('--theme', theme);
@@ -153,11 +184,13 @@ function parseHtmlMeta(htmlPath: string): { title: string; author: string; summa
if (titleMatch) title = titleMatch[1]!;
let author = '';
const authorMatch = content.match(/<meta\s+name=["']author["']\s+content=["']([^"']+)["']/i);
const authorMatch = content.match(/<meta\s+name=["']author["']\s+content=["']([^"']+)["']/i)
|| content.match(/<meta\s+content=["']([^"']+)["']\s+name=["']author["']/i);
if (authorMatch) author = authorMatch[1]!;
let summary = '';
const descMatch = content.match(/<meta\s+name=["']description["']\s+content=["']([^"']+)["']/i);
const descMatch = content.match(/<meta\s+name=["']description["']\s+content=["']([^"']+)["']/i)
|| content.match(/<meta\s+content=["']([^"']+)["']\s+name=["']description["']/i);
if (descMatch) summary = descMatch[1]!;
if (!summary) {
@@ -214,7 +247,7 @@ async function pressDeleteKey(session: ChromeSession): Promise<void> {
}
export async function postArticle(options: ArticleOptions): Promise<void> {
const { title, content, htmlFile, markdownFile, theme, author, summary, images = [], submit = false, profileDir } = options;
const { title, content, htmlFile, markdownFile, theme, author, summary, images = [], submit = false, profileDir, cdpPort } = options;
let { contentImages = [] } = options;
let effectiveTitle = title || '';
let effectiveAuthor = author || '';
@@ -248,16 +281,67 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
if (effectiveTitle && effectiveTitle.length > 64) throw new Error(`Title too long: ${effectiveTitle.length} chars (max 64)`);
if (!content && !effectiveHtmlFile) throw new Error('Either --content, --html, or --markdown is required');
const { cdp, chrome } = await launchChrome(WECHAT_URL, profileDir);
let cdp: CdpConnection;
let chrome: ReturnType<typeof import('node:child_process').spawn> | null = null;
// Try connecting to existing Chrome: explicit port > auto-detect > launch new
const portToTry = cdpPort ?? await findExistingChromeDebugPort();
if (portToTry) {
const existing = await tryConnectExisting(portToTry);
if (existing) {
console.log(`[cdp] Connected to existing Chrome on port ${portToTry}`);
cdp = existing;
} else {
console.log(`[cdp] Port ${portToTry} not available, launching new Chrome...`);
const launched = await launchChrome(WECHAT_URL, profileDir);
cdp = launched.cdp;
chrome = launched.chrome;
}
} else {
const launched = await launchChrome(WECHAT_URL, profileDir);
cdp = launched.cdp;
chrome = launched.chrome;
}
try {
console.log('[wechat] Waiting for page load...');
await sleep(3000);
let session = await getPageSession(cdp, 'mp.weixin.qq.com');
let session: ChromeSession;
if (!chrome) {
// Reusing existing Chrome: find an already-logged-in tab (has token in URL)
const allTargets = await cdp.send<{ targetInfos: Array<{ targetId: string; url: string; type: string }> }>('Target.getTargets');
const loggedInTab = allTargets.targetInfos.find(t => t.type === 'page' && t.url.includes('mp.weixin.qq.com') && t.url.includes('token='));
const wechatTab = loggedInTab || allTargets.targetInfos.find(t => t.type === 'page' && t.url.includes('mp.weixin.qq.com'));
if (wechatTab) {
console.log(`[wechat] Reusing existing tab: ${wechatTab.url.substring(0, 80)}...`);
const { sessionId: reuseSid } = await cdp.send<{ sessionId: string }>('Target.attachToTarget', { targetId: wechatTab.targetId, flatten: true });
await cdp.send('Page.enable', {}, { sessionId: reuseSid });
await cdp.send('Runtime.enable', {}, { sessionId: reuseSid });
await cdp.send('DOM.enable', {}, { sessionId: reuseSid });
session = { cdp, sessionId: reuseSid, targetId: wechatTab.targetId };
// Navigate to home if not already there
const currentUrl = await evaluate<string>(session, 'window.location.href');
if (!currentUrl.includes('/cgi-bin/home')) {
console.log('[wechat] Navigating to home...');
await evaluate(session, `window.location.href = '${WECHAT_URL}cgi-bin/home?t=home/index'`);
await sleep(5000);
}
} else {
// No WeChat tab found, create one
console.log('[wechat] No WeChat tab found, opening...');
await cdp.send('Target.createTarget', { url: WECHAT_URL });
await sleep(5000);
session = await getPageSession(cdp, 'mp.weixin.qq.com');
}
} else {
session = await getPageSession(cdp, 'mp.weixin.qq.com');
}
const url = await evaluate<string>(session, 'window.location.href');
if (!url.includes('/cgi-bin/home')) {
if (!url.includes('/cgi-bin/')) {
console.log('[wechat] Not logged in. Please scan QR code...');
const loggedIn = await waitForLogin(session);
if (!loggedIn) throw new Error('Login timeout');
@@ -265,6 +349,10 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
console.log('[wechat] Logged in.');
await sleep(2000);
// Wait for menu to be ready
const menuReady = await waitForElement(session, '.new-creation__menu', 20_000);
if (!menuReady) throw new Error('Home page menu did not load');
const targets = await cdp.send<{ targetInfos: Array<{ targetId: string; url: string; type: string }> }>('Target.getTargets');
const initialIds = new Set(targets.targetInfos.map(t => t.targetId));
@@ -293,8 +381,37 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
await evaluate(session, `document.querySelector('#author').value = ${JSON.stringify(effectiveAuthor)}; document.querySelector('#author').dispatchEvent(new Event('input', { bubbles: true }));`);
}
if (effectiveSummary) {
console.log(`[wechat] Filling summary: ${effectiveSummary}`);
await evaluate(session, `document.querySelector('#js_description').value = ${JSON.stringify(effectiveSummary)}; document.querySelector('#js_description').dispatchEvent(new Event('input', { bubbles: true }));`);
}
await sleep(500);
if (effectiveTitle) {
const actualTitle = await evaluate<string>(session, `document.querySelector('#title')?.value || ''`);
if (actualTitle === effectiveTitle) {
console.log('[wechat] Title verified OK.');
} else {
console.warn(`[wechat] Title verification failed. Expected: "${effectiveTitle}", got: "${actualTitle}"`);
}
}
if (effectiveSummary) {
const actualSummary = await evaluate<string>(session, `document.querySelector('#js_description')?.value || ''`);
if (actualSummary === effectiveSummary) {
console.log('[wechat] Summary verified OK.');
} else {
console.warn(`[wechat] Summary verification failed. Expected: "${effectiveSummary}", got: "${actualSummary}"`);
}
}
console.log('[wechat] Clicking on editor...');
await clickElement(session, '.ProseMirror');
await sleep(1000);
console.log('[wechat] Ensuring editor focus...');
await clickElement(session, '.ProseMirror');
await sleep(500);
if (effectiveHtmlFile && fs.existsSync(effectiveHtmlFile)) {
@@ -302,9 +419,23 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
await copyHtmlFromBrowser(cdp, effectiveHtmlFile);
await sleep(500);
console.log('[wechat] Pasting into editor...');
await pasteFromClipboardInEditor();
await pasteFromClipboardInEditor(session);
await sleep(3000);
const editorHasContent = await evaluate<boolean>(session, `
(function() {
const editor = document.querySelector('.ProseMirror');
if (!editor) return false;
const text = editor.innerText?.trim() || '';
return text.length > 0;
})()
`);
if (editorHasContent) {
console.log('[wechat] Body content verified OK.');
} else {
console.warn('[wechat] Body content verification failed: editor appears empty after paste.');
}
if (contentImages.length > 0) {
console.log(`[wechat] Inserting ${contentImages.length} images...`);
for (let i = 0; i < contentImages.length; i++) {
@@ -328,7 +459,7 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
await sleep(200);
console.log('[wechat] Pasting image...');
await pasteFromClipboardInEditor();
await pasteFromClipboardInEditor(session);
await sleep(3000);
}
console.log('[wechat] All images inserted.');
@@ -347,11 +478,20 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
console.log('[wechat] Typing content...');
await typeText(session, content);
await sleep(1000);
}
if (effectiveSummary) {
console.log(`[wechat] Filling summary: ${effectiveSummary}`);
await evaluate(session, `document.querySelector('#js_description').value = ${JSON.stringify(effectiveSummary)}; document.querySelector('#js_description').dispatchEvent(new Event('input', { bubbles: true }));`);
const editorHasContent = await evaluate<boolean>(session, `
(function() {
const editor = document.querySelector('.ProseMirror');
if (!editor) return false;
const text = editor.innerText?.trim() || '';
return text.length > 0;
})()
`);
if (editorHasContent) {
console.log('[wechat] Body content verified OK.');
} else {
console.warn('[wechat] Body content verification failed: editor appears empty after typing.');
}
}
console.log('[wechat] Saving as draft...');
@@ -389,6 +529,7 @@ Options:
--image <path> Content image, can repeat (only with --content)
--submit Save as draft
--profile <dir> Chrome profile directory
--cdp-port <port> Connect to existing Chrome debug port instead of launching new instance
Examples:
npx -y bun wechat-article.ts --markdown article.md
@@ -418,6 +559,7 @@ async function main(): Promise<void> {
let summary: string | undefined;
let submit = false;
let profileDir: string | undefined;
let cdpPort: number | undefined;
for (let i = 0; i < args.length; i++) {
const arg = args[i]!;
@@ -431,15 +573,18 @@ async function main(): Promise<void> {
else if (arg === '--image' && args[i + 1]) images.push(args[++i]!);
else if (arg === '--submit') submit = true;
else if (arg === '--profile' && args[i + 1]) profileDir = args[++i];
else if (arg === '--cdp-port' && args[i + 1]) cdpPort = parseInt(args[++i]!, 10);
}
if (!markdownFile && !htmlFile && !title) { console.error('Error: --title is required (or use --markdown/--html)'); process.exit(1); }
if (!markdownFile && !htmlFile && !content) { console.error('Error: --content, --html, or --markdown is required'); process.exit(1); }
await postArticle({ title: title || '', content, htmlFile, markdownFile, theme, author, summary, images, submit, profileDir });
await postArticle({ title: title || '', content, htmlFile, markdownFile, theme, author, summary, images, submit, profileDir, cdpPort });
}
await main().catch((err) => {
await main().then(() => {
process.exit(0);
}).catch((err) => {
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
process.exit(1);
});
@@ -67,7 +67,7 @@ Code blocks become blockquotes (X doesn't support code)
1. **Cover Image**: First image or `cover_image` from frontmatter
2. **Remote Images**: Automatically downloaded to temp directory
3. **Placeholders**: Images in content use `[[IMAGE_PLACEHOLDER_N]]` format
3. **Placeholders**: Images in content use `XIMGPH_N` format
4. **Insertion**: Placeholders are found, selected, and replaced with actual images
## Markdown to HTML Script
@@ -92,7 +92,7 @@ JSON output:
"coverImage": "/path/to/cover.jpg",
"contentImages": [
{
"placeholder": "[[IMAGE_PLACEHOLDER_1]]",
"placeholder": "XIMGPH_1",
"localPath": "/tmp/x-article-images/img.png",
"blockIndex": 5
}
+5 -5
View File
@@ -280,7 +280,7 @@ export async function parseMarkdown(
let imageCounter = 0;
const { html, totalBlocks } = convertMarkdownToHtml(body, (src, alt) => {
const placeholder = `[[IMAGE_PLACEHOLDER_${++imageCounter}]]`;
const placeholder = `XIMGPH_${++imageCounter}`;
const currentBlockIndex = images.length; // Will be set properly after HTML generation
images.push({ src, alt, blockIndex: -1 }); // blockIndex set later
@@ -292,7 +292,7 @@ export async function parseMarkdown(
let blockIdx = 0;
for (const line of htmlLines) {
for (let i = 0; i < images.length; i++) {
const placeholder = `[[IMAGE_PLACEHOLDER_${i + 1}]]`;
const placeholder = `XIMGPH_${i + 1}`;
if (line.includes(placeholder)) {
images[i]!.blockIndex = blockIdx;
}
@@ -312,7 +312,7 @@ export async function parseMarkdown(
// First image becomes cover if no cover specified
if (isFirstImage && !coverImagePath) {
coverImagePath = localPath;
coverPlaceholder = `[[IMAGE_PLACEHOLDER_${i + 1}]]`;
coverPlaceholder = `XIMGPH_${i + 1}`;
isFirstImage = false;
// Don't add to contentImages, it's the cover
continue;
@@ -320,7 +320,7 @@ export async function parseMarkdown(
isFirstImage = false;
contentImages.push({
placeholder: `[[IMAGE_PLACEHOLDER_${i + 1}]]`,
placeholder: `XIMGPH_${i + 1}`,
localPath,
originalPath: img.src,
blockIndex: img.blockIndex,
@@ -331,7 +331,7 @@ export async function parseMarkdown(
let finalHtml = html;
if (coverPlaceholder) {
// Remove the placeholder and its containing <p> tag
finalHtml = finalHtml.replace(new RegExp(`<p>${coverPlaceholder.replace(/[[\]]/g, '\\$&')}</p>\\n?`, 'g'), '');
finalHtml = finalHtml.replace(new RegExp(`<p>${coverPlaceholder}</p>\\n?`, 'g'), '');
}
// Resolve cover image path
+4 -2
View File
@@ -626,16 +626,18 @@ Flow:
| Action | Command | Manual Steps |
|--------|---------|--------------|
| **Edit** | `--regenerate N` | Update prompt → Regenerate image → Regenerate PDF |
| **Edit** | `--regenerate N` | **Update prompt file FIRST** → Regenerate image → Regenerate PDF |
| **Add** | Manual | Create prompt → Generate image → Renumber subsequent → Update outline → Regenerate PDF |
| **Delete** | Manual | Remove files → Renumber subsequent → Update outline → Regenerate PDF |
### Edit Single Slide
1. Update prompt in `prompts/NN-slide-{slug}.md`
1. **Update prompt file FIRST** in `prompts/NN-slide-{slug}.md`
2. Run: `/baoyu-slide-deck <dir> --regenerate N`
3. Or manually regenerate image + PDF
**IMPORTANT**: When updating slides, ALWAYS update the prompt file (`prompts/NN-slide-{slug}.md`) FIRST before regenerating. This ensures changes are documented and reproducible.
### Add New Slide
1. Create prompt at position: `prompts/NN-slide-{new-slug}.md`
+5 -4
View File
@@ -359,9 +359,8 @@ With confirmed outline + style + layout:
**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 main content.
Include a subtle watermark "[content]" positioned at [position].
The watermark should be legible but not distracting from the main content.
```
Reference: `references/config/watermark-guide.md`
@@ -404,10 +403,12 @@ Files:
| Action | Steps |
|--------|-------|
| **Edit** | Update prompt → Regenerate with same session ID |
| **Edit** | **Update prompt file FIRST** → Regenerate with same session ID |
| **Add** | Specify position → Create prompt → Generate → Renumber subsequent files (NN+1) → Update outline |
| **Delete** | Remove files → Renumber subsequent (NN-1) → Update outline |
**IMPORTANT**: When updating images, ALWAYS update the prompt file (`prompts/NN-{type}-[slug].md`) FIRST before regenerating. This ensures changes are documented and reproducible.
## Content Breakdown Principles
1. **Cover (Image 1)**: Hook + visual impact → `sparse` layout
@@ -15,7 +15,6 @@ watermark:
enabled: false
content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
opacity: 0.7 # 0.1-1.0
preferred_style:
name: null # Built-in or custom style name
@@ -46,7 +45,6 @@ custom_styles:
| `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_style.name` | string | null | Style name or null |
| `preferred_style.description` | string | "" | Custom notes/override |
| `preferred_layout` | string | null | Layout preference or null |
@@ -97,7 +95,6 @@ watermark:
enabled: true
content: "@myxhsaccount"
position: bottom-right
opacity: 0.5
preferred_style:
name: notion
@@ -28,15 +28,6 @@ description: Watermark configuration guide for baoyu-xhs-images
| `bottom-center` | Centered designs | Text-heavy bottom area |
| `top-right` | Bottom-heavy content | Title/header in top-right |
## Opacity Examples
| Opacity | Visual Effect | Use Case |
|---------|---------------|----------|
| 0.3 | Very subtle, barely visible | Clean aesthetic priority |
| 0.5 | Balanced, noticeable but not distracting | Default recommendation |
| 0.7 | Clearly visible | Brand recognition priority |
| 0.9 | Strong presence | Anti-copy protection |
## Content Format
| Format | Example | Style |
@@ -51,23 +42,21 @@ description: Watermark configuration guide for baoyu-xhs-images
1. **Consistency**: Use same watermark across all images in series
2. **Legibility**: Ensure watermark readable on both light/dark areas
3. **Size**: Keep subtle - should not distract from content
4. **Contrast**: Opacity may need adjustment per image background
## Prompt Integration
When watermark is enabled, add to 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 main content.
Include a subtle watermark "[content]" positioned at [position].
The watermark should be legible but not distracting from the main content.
```
## Common Issues
| Issue | Solution |
|-------|----------|
| Watermark invisible | Increase opacity or adjust position |
| Watermark too prominent | Decrease opacity (0.3-0.5) |
| Watermark invisible | Adjust position or check contrast |
| Watermark too prominent | Change position or reduce size |
| Watermark overlaps content | Change position |
| Inconsistent across images | Use session ID for consistency |