Compare commits

...

18 Commits

Author SHA1 Message Date
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
31 changed files with 1580 additions and 590 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
},
"metadata": {
"description": "Skills shared by Baoyu for improving daily work efficiency",
"version": "1.19.0"
"version": "1.22.0"
},
"plugins": [
{
+141 -39
View File
@@ -186,18 +186,61 @@ Japanese (CHANGELOG.ja.md):
- コネクションプールのメモリリークを修正
```
### 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 +251,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 +269,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 +404,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.
```
+51
View File
@@ -2,6 +2,57 @@
English | [中文](./CHANGELOG.zh.md)
## 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.
- `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
+51
View File
@@ -2,6 +2,57 @@
[English](./CHANGELOG.md) | 中文
## 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 键盘事件,实现跨平台支持。
- `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
### 新功能
+13 -6
View File
@@ -245,24 +245,31 @@ Generate professional infographics with 20 layout types and 17 visual styles. An
#### baoyu-cover-image
Generate cover images for articles with Type × Style two-dimension system.
Generate cover images for articles with 4 dimensions: Type × Style × Text × Mood.
```bash
# Auto-select type and style based on content
# Auto-select all dimensions based on content
/baoyu-cover-image path/to/article.md
# Specify type and/or style
# Quick mode: skip confirmation, use auto-selection
/baoyu-cover-image path/to/article.md --quick
# Specify dimensions
/baoyu-cover-image path/to/article.md --type conceptual --style blueprint
/baoyu-cover-image path/to/article.md --style warm
/baoyu-cover-image path/to/article.md --text title-subtitle --mood bold
# Specify aspect ratio (default: 2.35:1)
/baoyu-cover-image path/to/article.md --aspect 16:9
# Without title text
# Visual only (no title text)
/baoyu-cover-image path/to/article.md --no-title
```
Available types: `hero`, `conceptual`, `typography`, `metaphor`, `scene`, `minimal`
**Four Dimensions**:
- **Type**: `hero`, `conceptual`, `typography`, `metaphor`, `scene`, `minimal`
- **Style**: 20 built-in styles (see previews below)
- **Text**: `none`, `title-only` (default), `title-subtitle`, `text-rich`
- **Mood**: `subtle`, `balanced` (default), `bold`
Available styles: `elegant` (default), `blueprint`, `bold-editorial`, `chalkboard`, `dark-atmospheric`, `editorial-infographic`, `fantasy-animation`, `flat-doodle`, `intuition-machine`, `minimal`, `nature`, `notion`, `pixel-art`, `playful`, `retro`, `sketch-notes`, `vector-illustration`, `vintage`, `warm`, `watercolor`
+13 -6
View File
@@ -245,24 +245,31 @@ npx skills add jimliu/baoyu-skills
#### baoyu-cover-image
为文章生成封面图,支持类型 × 风格二维系统
为文章生成封面图,支持四维定制系统:类型 × 风格 × 文字 × 氛围
```bash
# 根据内容自动选择类型和风格
# 根据内容自动选择所有维度
/baoyu-cover-image path/to/article.md
# 指定类型和/或风格
# 快速模式:跳过确认,使用自动选择
/baoyu-cover-image path/to/article.md --quick
# 指定维度
/baoyu-cover-image path/to/article.md --type conceptual --style blueprint
/baoyu-cover-image path/to/article.md --style warm
/baoyu-cover-image path/to/article.md --text title-subtitle --mood bold
# 指定宽高比(默认:2.35:1
/baoyu-cover-image path/to/article.md --aspect 16:9
# 不含标题文字
# 纯视觉(不含标题文字
/baoyu-cover-image path/to/article.md --no-title
```
可用类型:`hero``conceptual``typography``metaphor``scene``minimal`
**四个维度**
- **类型 (Type)**`hero``conceptual``typography``metaphor``scene``minimal`
- **风格 (Style)**:20 种内置风格(见下方预览)
- **文字 (Text)**`none``title-only`(默认)、`title-subtitle``text-rich`
- **氛围 (Mood)**`subtle``balanced`(默认)、`bold`
可用风格:`elegant`(默认)、`blueprint``bold-editorial``chalkboard``dark-atmospheric``editorial-infographic``fantasy-animation``flat-doodle``intuition-machine``minimal``nature``notion``pixel-art``playful``retro``sketch-notes``vector-illustration``vintage``warm``watercolor`
+172 -301
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,60 @@ 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 |
| **Add** | Identify position → Create prompt → Generate → Update outline → Insert |
| **Delete** | Delete files → Remove reference → Update outline |
## 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
```
+6
View File
@@ -201,6 +201,12 @@ npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
--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 |
@@ -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 |
+314 -66
View File
@@ -1,23 +1,24 @@
---
name: baoyu-cover-image
description: Generates article cover images with 20 hand-drawn styles and auto-style selection. Supports cinematic (2.35:1), widescreen (16:9), and square (1:1) aspects. Use when user asks to "generate cover image", "create article cover", "make cover", or mentions "封面图".
description: Generates article cover images with 4 dimensions (type, style, text, mood) and 20 hand-drawn styles. Supports cinematic (2.35:1), widescreen (16:9), and square (1:1) aspects. Use when user asks to "generate cover image", "create article cover", "make cover", or mentions "封面图".
---
# Cover Image Generator
Generate elegant cover images for articles with multiple style options.
Generate elegant cover images for articles with 4-dimensional customization.
## Usage
```bash
# Auto-select style and aspect based on content
# Auto-select all dimensions based on content
/baoyu-cover-image path/to/article.md
# Specify style
/baoyu-cover-image article.md --style blueprint
# Quick mode: skip confirmation, use auto-selection
/baoyu-cover-image article.md --quick
# Specify aspect ratio
/baoyu-cover-image article.md --aspect 16:9
# Specify dimensions
/baoyu-cover-image article.md --type conceptual --style blueprint
/baoyu-cover-image article.md --text title-subtitle --mood bold
# Visual only (no title text)
/baoyu-cover-image article.md --no-title
@@ -27,7 +28,7 @@ Generate elegant cover images for articles with multiple style options.
[paste content]
# Direct input with options
/baoyu-cover-image --style notion --aspect 1:1
/baoyu-cover-image --style notion --aspect 1:1 --quick
[paste content]
```
@@ -35,20 +36,25 @@ Generate elegant cover images for articles with multiple style options.
| Option | Description |
|--------|-------------|
| `--type <name>` | Cover type (see Type Gallery) |
| `--type <name>` | Cover type: hero, conceptual, typography, metaphor, scene, minimal |
| `--style <name>` | Cover style (see Style Gallery) |
| `--aspect <ratio>` | 2.35:1 (default), 16:9, 1:1 |
| `--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
## Four Dimensions
| Dimension | Controls | Examples |
|-----------|----------|----------|
| **Type** | Visual composition, information structure | hero, conceptual, typography, metaphor, scene, minimal |
| **Style** | Visual aesthetics, colors, mood | elegant, blueprint, notion, warm, minimal, watercolor |
| Dimension | Controls | Values | Default |
|-----------|----------|--------|---------|
| **Type** | Visual composition, information structure | hero, conceptual, typography, metaphor, scene, minimal | auto |
| **Style** | Visual aesthetics, colors, technique | 20 built-in styles | auto |
| **Text** | Text density, information hierarchy | none, title-only, title-subtitle, text-rich | title-only |
| **Mood** | Emotional intensity, visual weight | subtle, balanced, bold | balanced |
Type × Style can be freely combined. Example: `--type conceptual --style blueprint` creates technical concept visualization with schematic aesthetics.
Dimensions can be freely combined. Example: `--type conceptual --style blueprint --text title-only --mood subtle` creates a calm technical concept visualization.
## Type Gallery
@@ -101,19 +107,6 @@ When `--type` is omitted, select based on content signals:
Style definitions: [references/styles/](references/styles/)
## Type × Style Compatibility
| | elegant | blueprint | notion | warm | minimal | watercolor | bold-editorial | dark-atmospheric |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| hero | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
| conceptual | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✗ | ✓ | ✓ |
| typography | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ |
| scene | ✓ | ✗ | ✗ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ |
| minimal | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✗ | ✓ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## Auto Style Selection
When `--style` is omitted, select based on content signals:
@@ -140,12 +133,102 @@ When `--style` is omitted, select based on content signals:
| Lifestyle, travel | `watercolor` |
| Business, professional | `elegant` |
## Text Dimension
| Value | Title | Subtitle | Tags | Use Case |
|-------|:-----:|:--------:|:----:|----------|
| `none` | - | - | - | Pure visual, no text |
| `title-only` | ✓ (≤8字) | - | - | Simple headline (default) |
| `title-subtitle` | ✓ | ✓ (≤15字) | - | Title + supporting context |
| `text-rich` | ✓ | ✓ | ✓ (2-4) | Information-dense |
Full guide: [references/dimensions/text.md](references/dimensions/text.md)
## Auto Text Selection
When `--text` is omitted, select based on content signals:
| Signals | Text Level |
|---------|------------|
| Visual-only, photography, abstract, art | `none` |
| Article, blog, standard cover | `title-only` |
| Series, tutorial, technical with context | `title-subtitle` |
| Announcement, features, multiple points, infographic | `text-rich` |
Default: `title-only`
## Mood Dimension
| Value | Contrast | Saturation | Weight | Use Case |
|-------|:--------:|:----------:|:------:|----------|
| `subtle` | Low | Muted | Light | Corporate, thought leadership |
| `balanced` | Medium | Normal | Medium | General articles (default) |
| `bold` | High | Vivid | Heavy | Announcements, promotions |
Full guide: [references/dimensions/mood.md](references/dimensions/mood.md)
## Auto Mood Selection
When `--mood` is omitted, select based on content signals:
| Signals | Mood Level |
|---------|------------|
| Professional, corporate, thought leadership, academic, luxury | `subtle` |
| General, educational, standard, blog, documentation | `balanced` |
| Launch, announcement, promotion, event, gaming, entertainment | `bold` |
Default: `balanced`
## Compatibility Matrices
### Type × Style
| | elegant | blueprint | notion | warm | minimal | watercolor | bold-editorial | dark-atmospheric |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| hero | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
| conceptual | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✗ | ✓ | ✓ |
| typography | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ |
| scene | ✓ | ✗ | ✗ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ |
| minimal | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✗ | ✓ |
### Type × Text
| | none | title-only | title-subtitle | text-rich |
|---|:---:|:---:|:---:|:---:|
| hero | ✓ | ✓✓ | ✓✓ | ✓ |
| conceptual | ✓✓ | ✓✓ | ✓ | ✓ |
| typography | ✗ | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✓ | ✓ | ✗ |
| scene | ✓✓ | ✓ | ✓ | ✗ |
| minimal | ✓✓ | ✓✓ | ✓ | ✗ |
### Type × Mood
| | subtle | balanced | bold |
|---|:---:|:---:|:---:|
| hero | ✓ | ✓✓ | ✓✓ |
| conceptual | ✓✓ | ✓✓ | ✓ |
| typography | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✓✓ | ✓ |
| scene | ✓✓ | ✓✓ | ✓ |
| minimal | ✓✓ | ✓✓ | ✗ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## File Structure
Each session creates an independent directory named by content slug:
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
@@ -174,8 +257,8 @@ 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 (4 dimensions) ⚠️ REQUIRED unless --quick or all specified
- [ ] Step 3: Create prompt
- [ ] Step 4: Generate image
- [ ] Step 5: Completion report
@@ -184,7 +267,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: 4 Dimensions] → Prompt → Generate → Complete
(skip if --quick or all specified)
```
### Step 0: Load Preferences (EXTEND.md) ⚠️
@@ -226,7 +311,11 @@ Preferences loaded from [project/user]:
• Watermark: [enabled/disabled] [content if enabled]
• Type: [preferred_type or "auto"]
• Style: [preferred_style or "auto"]
• Text: [preferred_text or "title-only"]
• Mood: [preferred_mood or "balanced"]
• Aspect: [default_aspect]
• Output: [default_output_dir or "not set — will ask in Step 1.5"]
• Quick mode: [enabled/disabled]
• Language: [language or "auto"]
```
@@ -276,15 +365,43 @@ options:
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"
```
**Q5: Save Location**
Note: More ratios (4:3, 3:2) available during generation. This sets the default recommendation.
**Q5: 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"
```
**Q6: 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"
```
**Q7: Save Location**
```yaml
header: "Save"
question: "Where to save preferences?"
@@ -299,7 +416,7 @@ options:
Full setup details: `references/config/first-time-setup.md`
**EXTEND.md Supports**: Watermark | Preferred type | Preferred style | Default aspect ratio | Custom style definitions | Language preference
**EXTEND.md Supports**: Watermark | Preferred type | Preferred style | Preferred text | Preferred mood | Default aspect ratio | Default output directory | Quick mode | Custom style definitions | Language preference
Schema: `references/config/preferences-schema.md`
@@ -321,9 +438,57 @@ Read source content, save it if needed, and perform analysis.
- Note user's input language (from conversation)
- Compare with language preference in EXTEND.md
**1.5 Determine Output Directory** ⚠️
**MUST ask user when `default_output_dir` is not set in EXTEND.md and input is a file path.**
| Input | Behavior |
|-------|----------|
| File path + preference set | Use configured `default_output_dir` |
| File path + **no preference** | ⚠️ **MUST** ask with AskUserQuestion ↓ |
| Pasted content | `cover-image/{topic-slug}/` (always independent) |
`default_output_dir` preference values:
| Preference Value | Output Path |
|------------------|-------------|
| `same-dir` | `{article-dir}/` |
| `imgs-subdir` | `{article-dir}/imgs/` |
| `independent` | `cover-image/{topic-slug}/` |
**AskUserQuestion** (when no preference, file path input only):
- `{article-dir}/imgs/` - Images subdirectory near article
- `{article-dir}/` - Same directory as article
- `cover-image/{topic-slug}/` - Independent directory
- Save as default - Remember this choice for future runs
### Step 2: Confirm Options ⚠️
**Purpose**: Validate type, style and aspect ratio. **Do NOT skip.**
**Purpose**: Validate all 4 dimensions + aspect ratio.
**Skip Conditions**:
| Condition | Skipped Questions | Still Asked |
|-----------|-------------------|-------------|
| `--quick` flag | Type, Style, Text, Mood | **Aspect Ratio** (unless `--aspect` specified) |
| All 4 dimensions + `--aspect` specified | All | None |
| `quick_mode: true` in EXTEND.md | Type, Style, Text, Mood | **Aspect Ratio** (unless `--aspect` specified) |
| Otherwise | None | All 5 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 4 dimensions):
```
Quick Mode: Auto-selected dimensions
• Type: [type] ([reason])
• Style: [style] ([reason])
• Text: [text] ([reason])
• Mood: [mood] ([reason])
[Then ask Question 5: Aspect Ratio]
```
**Confirmation Flow** (when NOT skipping):
**Language**: Auto-determined (user's input language > saved preference > source language). No need to ask.
@@ -364,50 +529,118 @@ options:
description: "[reason]"
```
**Question 3: Aspect Ratio** (if not specified via `--aspect`)
**Question 3: Text** (if not specified via `--text`)
- Based on selected Type, show compatible text levels (✓✓ first from compatibility matrix)
```yaml
header: "Text"
question: "Text density level?"
multiSelect: false
options:
- label: "title-only (Recommended)"
description: "Simple headline, ≤8 characters"
- label: "none"
description: "Pure visual, no text elements"
- label: "title-subtitle"
description: "Title + supporting context"
- label: "text-rich"
description: "Title + subtitle + keyword tags"
```
**Question 4: Mood** (if not specified via `--mood`)
- Based on content analysis, show recommended mood
```yaml
header: "Mood"
question: "Emotional intensity?"
multiSelect: false
options:
- label: "balanced (Recommended)"
description: "Medium contrast and saturation, versatile"
- label: "subtle"
description: "Low contrast, muted colors, calm"
- label: "bold"
description: "High contrast, vivid colors, dynamic"
```
**Question 5: Aspect Ratio** (ALWAYS ask unless `--aspect` specified via CLI)
Note: Even if user has a preset in EXTEND.md, still ask this question. The preset is shown as the recommended option.
```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: "[user preset or 16:9] (Recommended)"
description: "[based on preset or default: Standard widescreen, versatile]"
- label: "2.35:1"
description: "Cinematic widescreen - article headers, blog posts"
- label: "4:3"
description: "Traditional screen - PPT slides, classic displays"
- label: "3:2"
description: "Photography ratio - blog articles, Medium posts"
- label: "1:1"
description: "Square, social media friendly"
description: "Square - Instagram, WeChat moments, social cards"
- label: "3:4"
description: "Portrait - Xiaohongshu, Pinterest, mobile-first content"
```
**After response**: Proceed to Step 3 with confirmed type + style + aspect ratio.
**After response**: Proceed to Step 3 with confirmed dimensions.
### Step 3: Create Prompt
Save to `prompts/cover.md`:
```markdown
Cover theme: [2-3 words]
# Content Context
Article title: [full original title from source]
Content summary: [2-3 sentence summary of key points and themes]
Keywords: [5-8 key terms extracted from content]
# Visual Design
Cover theme: [2-3 words visual interpretation]
Type: [confirmed type]
Style: [confirmed style]
Text level: [confirmed text level]
Mood: [confirmed mood]
Aspect ratio: [confirmed ratio]
Title text: [max 8 chars, or "none" if --no-title]
Language: [confirmed language]
# Text Elements
[Based on text level:]
- none: "No text elements"
- title-only: "Title: [max 8 chars headline]"
- title-subtitle: "Title: [headline] / Subtitle: [max 15 chars context]"
- text-rich: "Title: [headline] / Subtitle: [context] / Tags: [2-4 keywords]"
# Mood Application
[Based on mood level:]
- subtle: "Use low contrast, muted colors, light visual weight, calm aesthetic"
- balanced: "Use medium contrast, normal saturation, balanced visual weight"
- bold: "Use high contrast, vivid saturated colors, heavy visual weight, dynamic energy"
# Composition
Type composition:
- [Type-specific layout and structure]
Visual composition:
- Main visual: [type + style appropriate metaphor]
- Main visual: [metaphor derived from content meaning]
- Layout: [positioning based on type and aspect ratio]
- Decorative: [style elements]
- Decorative: [style elements that reinforce content theme]
Color scheme: [primary, background, accent from style]
Color scheme: [primary, background, accent from style, adjusted by mood]
Type notes: [key characteristics from type definition]
Style notes: [key characteristics from style definition]
[Watermark section if enabled]
```
**Content-Driven Design**:
- Article title and summary inform the visual metaphor choice
- Keywords guide decorative elements and symbols
- The skill controls visual style; the content drives meaning
**Type-Specific Composition**:
| Type | Composition Guidelines |
@@ -419,7 +652,7 @@ Style notes: [key characteristics from style definition]
| `scene` | Atmospheric environment, narrative elements, mood-setting lighting and colors |
| `minimal` | Single focal element, generous whitespace (60%+), essential shapes only |
**Title guidelines** (if included):
**Title guidelines** (when text level includes title):
- Max 8 characters, punchy headline
- Use hooks: numbers, questions, contrasts
- Match confirmed language
@@ -427,14 +660,17 @@ Style notes: [key characteristics from style definition]
**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.
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`
### Step 4: Generate Image
**Backup Existing Cover** (if regenerating):
If `cover.png` already exists in the output directory:
- Rename to `cover-backup-YYYYMMDD-HHMMSS.png`
**Image Generation Skill Selection**:
1. Check available image generation skills
2. If multiple skills available, ask user preference
@@ -453,8 +689,10 @@ Cover Generated!
Topic: [topic]
Type: [type name]
Style: [style name]
Text: [text level]
Mood: [mood level]
Aspect: [ratio]
Title: [title or "visual only"]
Title: [title text or "visual only"]
Language: [lang]
Watermark: [enabled/disabled]
Location: [directory path]
@@ -463,29 +701,39 @@ Files:
✓ source-{slug}.{ext}
✓ prompts/cover.md
✓ cover.png
[✓ cover-backup-{timestamp}.png (if regenerated)]
```
## Image Modification
| Action | Steps |
|--------|-------|
| **Regenerate** | Update prompt → Regenerate with same settings |
| **Change type** | Confirm new type → Update prompt → Regenerate |
| **Change style** | Confirm new style → Update prompt → Regenerate |
| **Change aspect** | Confirm new aspect → Update prompt → Regenerate |
| **Regenerate** | Backup existing → Update prompt → Regenerate with same settings |
| **Change type** | Backup existing → Confirm new type → Update prompt → Regenerate |
| **Change style** | Backup existing → Confirm new style → Update prompt → Regenerate |
| **Change text** | Backup existing → Confirm new text level → Update prompt → Regenerate |
| **Change mood** | Backup existing → Confirm new mood → Update prompt → Regenerate |
| **Change aspect** | Backup existing → Confirm new aspect → Update prompt → Regenerate |
All modifications automatically backup the existing `cover.png` before regenerating.
## Notes
- Cover must be readable at small preview sizes
- Visual metaphors > literal representations
- Title: max 8 chars, readable, impactful
- **Two confirmation points**: Step 0 (first-time setup if no EXTEND.md) + Step 2 (options) - do NOT skip
- **Two confirmation points**: Step 0 (first-time setup if no EXTEND.md) + Step 2 (options) - can skip Step 2 with `--quick`
- Use confirmed language for title text
- Maintain watermark consistency if enabled
- Check Type × Style compatibility matrix when selecting combinations
- Check compatibility matrices when selecting combinations
- `--no-title` is preserved as alias for `--text none`
## References
**Dimensions**:
- `references/dimensions/text.md` - Text density dimension
- `references/dimensions/mood.md` - Mood intensity dimension
**Styles**: `references/styles/<name>.md` - Style definitions
**Config**:
@@ -13,14 +13,49 @@ Create a WeChat article cover image following these guidelines:
- Ample whitespace, highlight core message, avoid cluttered layouts
- Main visual elements centered or slightly left (leave right side for title area if title included)
## Four Dimensions
### Type (Visual Composition)
- `hero`: Large focal visual (60-70% area), dramatic composition
- `conceptual`: Abstract shapes, information hierarchy, clean zones
- `typography`: Title as primary element (40%+ area), minimal visuals
- `metaphor`: Concrete object representing abstract idea, symbolic elements
- `scene`: Atmospheric environment, narrative elements, mood lighting
- `minimal`: Single focal element, generous whitespace (60%+)
### Style (Visual Aesthetics)
Apply the specified style's color palette, visual elements, and typography characteristics.
### Text (Density Level)
- `none`: No text elements, full visual area
- `title-only`: Single headline (≤8 characters), 85% visual area
- `title-subtitle`: Title + context (≤15 chars), 75% visual area
- `text-rich`: Title + subtitle + 2-4 keyword tags, 60% visual area
### Mood (Emotional Intensity)
- `subtle`: Low contrast, muted/desaturated colors, light visual weight, calm aesthetic
- `balanced`: Medium contrast, normal saturation, balanced visual weight
- `bold`: High contrast, vivid/saturated colors, heavy visual weight, dynamic energy
## Text Style (When Title Included)
- **ALL text MUST be hand-drawn style**
- Title text: Large, eye-catching, max 8 characters
- May include 1 line of subtitle or keyword tags
- Subtitle: Secondary, max 15 characters (if title-subtitle or text-rich)
- Tags: 2-4 keyword badges (if text-rich)
- Font style harmonizes with illustration style
- **DO NOT use realistic or computer-generated fonts**
## Mood Application
Apply mood adjustments to the base style:
| Mood | Contrast | Saturation | Weight |
|------|----------|------------|--------|
| subtle | Reduce 20-30% | Desaturate 20-30% | Lighter strokes/fills |
| balanced | Standard | Standard | Standard |
| bold | Increase 20-30% | Increase 20-30% | Heavier strokes/fills |
## Language
- Use the same language as the content provided below for any text elements
@@ -93,7 +93,19 @@ options:
description: "Square, social media friendly"
```
### Question 5: Save Location
### Question 5: Quick Mode
```
header: "Quick"
question: "Enable quick mode by default?"
options:
- label: "No (Recommended)"
description: "Confirm dimension choices each time"
- label: "Yes"
description: "Skip confirmation, use auto-selection"
```
### Question 6: Save Location
```
header: "Save"
@@ -123,7 +135,7 @@ options:
```yaml
---
version: 1
version: 2
watermark:
enabled: [true/false]
content: "[user input or empty]"
@@ -131,12 +143,25 @@ watermark:
opacity: 0.7
preferred_type: [selected type or null]
preferred_style: [selected style or null]
preferred_text: title-only
preferred_mood: balanced
default_aspect: [2.35:1/16:9/1:1]
quick_mode: [true/false]
language: null
custom_styles: []
---
```
## New Fields in v2
| Field | Default | Description |
|-------|---------|-------------|
| `preferred_text` | title-only | Text density (none, title-only, title-subtitle, text-rich) |
| `preferred_mood` | balanced | Mood intensity (subtle, balanced, bold) |
| `quick_mode` | false | Skip confirmation step when true |
Note: Text and Mood preferences use sensible defaults (title-only, balanced) and don't require setup questions. Users can modify these in EXTEND.md directly.
## Modifying Preferences Later
Users can edit EXTEND.md directly or run setup again:
@@ -9,20 +9,25 @@ description: EXTEND.md YAML schema for baoyu-cover-image user preferences
```yaml
---
version: 1
version: 2
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_text: title-only # none|title-only|title-subtitle|text-rich
preferred_mood: balanced # subtle|balanced|bold
default_aspect: "2.35:1" # 2.35:1|16:9|1:1
quick_mode: false # Skip confirmation when true
language: null # zh|en|ja|ko|auto (null = auto-detect)
custom_styles:
@@ -42,14 +47,16 @@ custom_styles:
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `version` | int | 1 | Schema version |
| `version` | int | 2 | Schema version |
| `watermark.enabled` | bool | false | Enable watermark |
| `watermark.content` | string | "" | Watermark text (@username or custom) |
| `watermark.position` | enum | bottom-right | Position on image |
| `watermark.opacity` | float | 0.7 | Transparency (0.1-1.0) |
| `preferred_type` | string | null | Type name or null for auto |
| `preferred_style` | string | null | Style name or null for auto |
| `preferred_text` | string | title-only | Text density level |
| `preferred_mood` | string | balanced | Mood intensity level |
| `default_aspect` | string | "2.35:1" | Default aspect ratio |
| `quick_mode` | bool | false | Skip confirmation step |
| `language` | string | null | Output language (null = auto-detect) |
| `custom_styles` | array | [] | User-defined styles |
@@ -64,6 +71,23 @@ custom_styles:
| `scene` | Atmospheric scene, narrative feel |
| `minimal` | Minimalist composition, generous whitespace |
## Text Options
| Value | Description |
|-------|-------------|
| `none` | Pure visual, no text elements |
| `title-only` | Single headline (≤8 characters) |
| `title-subtitle` | Title + subtitle (≤15 characters) |
| `text-rich` | Title + subtitle + keyword tags (2-4) |
## Mood Options
| Value | Description |
|-------|-------------|
| `subtle` | Low contrast, muted colors, calm aesthetic |
| `balanced` | Medium contrast, normal saturation, versatile |
| `bold` | High contrast, vivid colors, dynamic energy |
## Position Options
| Value | Description |
@@ -98,12 +122,15 @@ custom_styles:
```yaml
---
version: 1
version: 2
watermark:
enabled: true
content: "@myhandle"
preferred_type: null
preferred_style: elegant
preferred_text: title-only
preferred_mood: balanced
quick_mode: false
---
```
@@ -111,19 +138,24 @@ preferred_style: elegant
```yaml
---
version: 1
version: 2
watermark:
enabled: true
content: "myblog.com"
position: bottom-right
opacity: 0.5
preferred_type: conceptual
preferred_style: blueprint
preferred_text: title-subtitle
preferred_mood: subtle
default_aspect: "16:9"
quick_mode: true
language: en
custom_styles:
@@ -138,3 +170,16 @@ custom_styles:
best_for: "SaaS, enterprise, technical"
---
```
## Migration from v1
When loading v1 schema, auto-upgrade:
| v1 Field | v2 Field | Default Value |
|----------|----------|---------------|
| (missing) | `version` | 2 |
| (missing) | `preferred_text` | title-only |
| (missing) | `preferred_mood` | balanced |
| (missing) | `quick_mode` | false |
v1 `--no-title` flag maps to `preferred_text: none`.
@@ -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,125 @@
---
name: mood-dimension
description: Emotional intensity dimension for cover images
---
# Mood Dimension
Controls emotional intensity and visual weight of cover images.
## Values
| Value | Contrast | Saturation | Weight | Energy |
|-------|:--------:|:----------:|:------:|:------:|
| `subtle` | Low | Muted | Light | Calm |
| `balanced` | Medium | Normal | Medium | Moderate |
| `bold` | High | Vivid | Heavy | Dynamic |
## Detail
### subtle
Calm, understated visual presence.
**Characteristics**:
- Low contrast between elements
- Muted, desaturated colors
- Light visual weight
- Gentle, refined aesthetic
- Soft edges and transitions
**Use Cases**:
- Thought leadership content
- Professional/corporate communications
- Meditation, wellness topics
- Academic or scholarly articles
- Luxury brand aesthetics
**Color Guidance**:
- Pastels, earth tones, neutrals
- Low saturation (30-50%)
- Soft gradients
- Minimal color variety (2-3 colors)
### balanced
Versatile, harmonious visual presence.
**Characteristics**:
- Medium contrast
- Natural saturation levels
- Balanced visual weight
- Clear but not aggressive
- Standard aesthetic approach
**Use Cases**:
- General articles (default)
- Most blog content
- Educational material
- Product documentation
- News and updates
**Color Guidance**:
- Standard saturation (50-70%)
- Complementary color schemes
- Clear foreground/background separation
- Moderate color variety (3-4 colors)
### bold
Dynamic, high-impact visual presence.
**Characteristics**:
- High contrast between elements
- Vivid, saturated colors
- Heavy visual weight
- Energetic, attention-grabbing
- Sharp edges and strong shapes
**Use Cases**:
- Product launches
- Promotional announcements
- Event marketing
- Call-to-action content
- Entertainment/gaming topics
**Color Guidance**:
- High saturation (70-100%)
- Vibrant, primary colors
- Strong contrast ratios
- Dynamic color combinations (4+ colors)
## Type Compatibility
| Type | subtle | balanced | bold |
|------|:------:|:--------:|:----:|
| hero | ✓ | ✓✓ | ✓✓ |
| conceptual | ✓✓ | ✓✓ | ✓ |
| typography | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✓✓ | ✓ |
| scene | ✓✓ | ✓✓ | ✓ |
| minimal | ✓✓ | ✓✓ | ✗ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## Style Interaction
Mood modifies the base style characteristics:
| Style Category | subtle | balanced | bold |
|----------------|--------|----------|------|
| Technical (blueprint, notion) | Lighter lines, softer colors | Standard rendering | Stronger contrast, sharper edges |
| Artistic (watercolor, sketch-notes) | More whitespace, lighter strokes | Standard rendering | Deeper colors, heavier strokes |
| Editorial (bold-editorial, dark-atmospheric) | Reduced contrast, softer tones | Standard rendering | Maximum impact, vivid colors |
## Auto Selection
When `--mood` is omitted, select based on signals:
| Signals | Mood Level |
|---------|------------|
| Professional, corporate, thought leadership, academic, luxury | `subtle` |
| General, educational, standard, blog, documentation | `balanced` |
| Launch, announcement, promotion, event, gaming, entertainment | `bold` |
Default: `balanced`
@@ -0,0 +1,122 @@
---
name: text-dimension
description: Text density dimension for cover images
---
# Text Dimension
Controls text density and information hierarchy on cover images.
## Values
| Value | Title | Subtitle | Tags | Visual Area |
|-------|:-----:|:--------:|:----:|:-----------:|
| `none` | - | - | - | 100% |
| `title-only` | ✓ (≤8字) | - | - | 85% |
| `title-subtitle` | ✓ | ✓ (≤15字) | - | 75% |
| `text-rich` | ✓ | ✓ | ✓ (2-4) | 60% |
## Detail
### none
Pure visual cover with no text elements.
**Use Cases**:
- Photography-focused covers
- Abstract art pieces
- Visual-only social sharing
- When title added externally
**Composition**:
- Full visual area available
- No reserved text zones
- Emphasis on visual metaphor
### title-only
Single headline, maximum impact.
**Use Cases**:
- Most article covers (default)
- Clear single message
- Strong brand recognition
**Composition**:
- Title: ≤8 characters, prominent
- Reserved zone: top or bottom 15%
- Visual supports title message
**Title Guidelines**:
- Punchy, action-oriented
- Numbers, questions, contrasts work well
- Match content language
### title-subtitle
Title with supporting context.
**Use Cases**:
- Technical articles needing clarification
- Series with episode/part info
- Content with dual messages
**Composition**:
- Title: ≤8 characters, primary
- Subtitle: ≤15 characters, secondary
- Reserved zone: 25%
- Clear hierarchy between title/subtitle
**Subtitle Guidelines**:
- Clarify or contextualize title
- Can include series name, author, date
- Smaller, less prominent than title
### text-rich
Information-dense cover with multiple text elements.
**Use Cases**:
- Infographic-style covers
- Event announcements with details
- Promotional material with features
- Content with multiple key points
**Composition**:
- Title: primary focus
- Subtitle: supporting info
- Tags: 2-4 keyword labels
- Reserved zone: 40%
- Clear visual hierarchy
**Tag Guidelines**:
- 2-4 tags maximum
- Short keywords (1-2 words each)
- Positioned as badges/labels
- Can highlight: category, date, author, key features
## Type Compatibility
| Type | none | title-only | title-subtitle | text-rich |
|------|:----:|:----------:|:--------------:|:---------:|
| hero | ✓ | ✓✓ | ✓✓ | ✓ |
| conceptual | ✓✓ | ✓✓ | ✓ | ✓ |
| typography | ✗ | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✓ | ✓ | ✗ |
| scene | ✓✓ | ✓ | ✓ | ✗ |
| minimal | ✓✓ | ✓✓ | ✓ | ✗ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## Auto Selection
When `--text` is omitted, select based on signals:
| Signals | Text Level |
|---------|------------|
| Visual-only, photography, abstract, art | `none` |
| Article, blog, standard cover | `title-only` |
| Series, tutorial, technical with context | `title-subtitle` |
| Announcement, features, multiple points, infographic | `text-rich` |
Default: `title-only`
+20 -1
View File
@@ -1,6 +1,6 @@
---
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 and Google APIs. Supports text-to-image, reference images, aspect ratios, and parallel generation (recommended 4 concurrent subagents). Use when user asks to generate, create, or draw images.
---
# Image Generation (AI SDK)
@@ -118,6 +118,25 @@ Supported: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `2.35:1`
- Google Imagen: uses `aspectRatio` parameter
- OpenAI: maps to closest supported size
## Parallel Generation
Supports concurrent image generation via background subagents for batch operations.
| Setting | Value |
|---------|-------|
| Recommended concurrency | 4 subagents |
| Max concurrency | 8 subagents |
| Use case | Batch generation (slides, comics, infographics) |
**Agent Implementation**:
```
# 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
```
**Best Practice**: When generating 4+ images, spawn background subagents (recommended 4 concurrent) instead of sequential execution.
## Error Handling
- Missing API key → error with setup instructions
@@ -2,19 +2,25 @@ import path from "node:path";
import { readFile } from "node:fs/promises";
import type { CliArgs } from "../types";
const GOOGLE_MULTIMODAL_MODELS = ["gemini-3-pro-image-preview"];
const GOOGLE_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 {
return GOOGLE_MULTIMODAL_MODELS.some((m) => model.includes(m));
const normalized = normalizeGoogleModelId(model);
return GOOGLE_MULTIMODAL_MODELS.some((m) => normalized.includes(m));
}
function isGoogleImagen(model: string): boolean {
return GOOGLE_IMAGEN_MODELS.some((m) => model.includes(m));
const normalized = normalizeGoogleModelId(model);
return GOOGLE_IMAGEN_MODELS.some((m) => normalized.includes(m));
}
function getGoogleApiKey(): string | null {
@@ -26,6 +32,44 @@ function getGoogleImageSize(args: CliArgs): "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) {
@@ -37,6 +81,11 @@ function buildPromptWithAspect(prompt: string, ar: string | null, quality: CliAr
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();
@@ -47,53 +96,74 @@ async function readImageAsBase64(p: string): Promise<{ data: string; mimeType: s
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 { GoogleGenAI } = await import("@google/genai");
const apiKey = getGoogleApiKey();
if (!apiKey) throw new Error("GOOGLE_API_KEY or GEMINI_API_KEY is required");
const ai = new GoogleGenAI({
apiKey,
httpOptions: {
baseUrl: process.env.GOOGLE_BASE_URL || undefined,
},
});
const input: Array<{ type: "text" | "image"; text?: string; data?: string; mime_type?: string }> = [];
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);
input.push({ type: "image", data, mime_type: mimeType });
parts.push({ inlineData: { data, mimeType } });
}
input.push({ type: "text", text: prompt });
parts.push({ text: promptWithAspect });
const imageConfig: { image_size: "1K" | "2K" | "4K"; aspect_ratio?: string } = {
image_size: getGoogleImageSize(args),
const imageConfig: { imageSize: "1K" | "2K" | "4K" } = {
imageSize: getGoogleImageSize(args),
};
if (args.aspectRatio) {
imageConfig.aspect_ratio = args.aspectRatio;
}
console.log("Generating image with Gemini...", imageConfig);
const interaction = await ai.interactions.create({
model,
input,
response_modalities: ["image"],
generation_config: {
image_config: imageConfig,
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.");
for (const output of interaction.outputs || []) {
if (output.type === "image" && output.data) {
return Uint8Array.from(Buffer.from(output.data, "base64"));
}
}
const imageData = extractInlineImageData(response);
if (imageData) return Uint8Array.from(Buffer.from(imageData, "base64"));
throw new Error("No image in response");
}
@@ -103,30 +173,40 @@ async function generateWithImagen(
model: string,
args: CliArgs
): Promise<Uint8Array> {
const { experimental_generateImage: generateImage } = await import("ai");
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
const google = createGoogleGenerativeAI({
apiKey: getGoogleApiKey() || undefined,
baseURL: process.env.GOOGLE_BASE_URL,
});
const fullPrompt = buildPromptWithAspect(prompt, args.aspectRatio, args.quality);
const imageSize = getGoogleImageSize(args);
if (imageSize === "4K") {
console.error("Warning: Imagen models do not support 4K imageSize, using 2K instead.");
}
const result = await generateImage({
model: google.image(model),
prompt: fullPrompt,
n: args.n,
aspectRatio: args.aspectRatio || undefined,
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 img = result.images[0];
if (!img) throw new Error("No image in response");
const imageData = extractPredictedImageData(response);
if (imageData) return Uint8Array.from(Buffer.from(imageData, "base64"));
if (img.uint8Array) return img.uint8Array;
if (img.base64) return Uint8Array.from(Buffer.from(img.base64, "base64"));
throw new Error("Cannot extract image data");
throw new Error("No image in response");
}
export async function generateImage(
+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
@@ -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,17 +112,11 @@ 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 ?? '';
@@ -129,9 +124,9 @@ async function parseMarkdownForWechat(
const h1Match = body.match(/^#\s+(.+)$/m);
if (h1Match) title = h1Match[1]!;
}
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 +156,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 +224,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 +260,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 +269,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,6 +2,7 @@ import fs from 'node:fs';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import process from 'node:process';
import { fileURLToPath } from 'node:url';
import { launchChrome, getPageSession, waitForNewTab, clickElement, typeText, evaluate, sleep, type ChromeSession, type CdpConnection } from './cdp.ts';
const WECHAT_URL = 'https://mp.weixin.qq.com/';
@@ -65,8 +66,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 +80,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 +134,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);
@@ -295,6 +315,10 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
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,7 +326,7 @@ 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);
if (contentImages.length > 0) {
@@ -328,7 +352,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.');
@@ -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
+2 -3
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`
@@ -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 |