mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-12 22:09:48 +08:00
Compare commits
262 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1cb54420e0 | |||
| 6363bd83e2 | |||
| 7b8247544d | |||
| a9576ebc67 | |||
| 79ca378229 | |||
| e1a1fe23cb | |||
| 3d85a7e663 | |||
| 10aabb39f8 | |||
| 661c74cce4 | |||
| 82445f2a39 | |||
| df9ce95c46 | |||
| 88b433d565 | |||
| 5acef7151b | |||
| e4d4ec8334 | |||
| 4935dc8b1a | |||
| c43ed57ffc | |||
| d96368492e | |||
| cfc647fd38 | |||
| 5144335916 | |||
| fa82c3d910 | |||
| 90730bc551 | |||
| 154ccfe3ff | |||
| 93efa5aeb1 | |||
| 40ccbed74a | |||
| fbf2bc02c5 | |||
| a37c80e142 | |||
| 2e68aa64a4 | |||
| a702513487 | |||
| 059ebc759c | |||
| d4a5a34865 | |||
| b3b2f8ea36 | |||
| f19ed00cb2 | |||
| 8e58855047 | |||
| fdfb0e2dfe | |||
| b4e319ba8f | |||
| 691a9c2272 | |||
| 16db99a3ef | |||
| adb587439d | |||
| 6b2b28b421 | |||
| 0df63c3a0b | |||
| 7834d632e0 | |||
| ca9a0a1404 | |||
| 366e7b5403 | |||
| 709e026be1 | |||
| f6cef6bcbb | |||
| 4300e6899c | |||
| 4435366898 | |||
| 69cd89184e | |||
| 5771e6d8c5 | |||
| 970f5e741a | |||
| 8cea550627 | |||
| 82029160d1 | |||
| 46af919634 | |||
| 5560db595a | |||
| 3b031c7768 | |||
| 39c7e86a8d | |||
| f00ca11d9a | |||
| 87c2509268 | |||
| 20bf6d9ad8 | |||
| 33bc0a6255 | |||
| c15a44b439 | |||
| 6e533f938f | |||
| c60eb85629 | |||
| 20d2a78f87 | |||
| bb63ee2a2e | |||
| 5fc697166d | |||
| 1ed204bd5a | |||
| be2cbecfb0 | |||
| bce96e411d | |||
| fe404c493d | |||
| 6dd754ccdd | |||
| 1f4415c8ff | |||
| b25eb87bb4 | |||
| dbf35503a3 | |||
| 36ccbd0300 | |||
| bc878e5157 | |||
| 727375afa3 | |||
| f2c914887a | |||
| 9f76a96741 | |||
| 67e3e11cce | |||
| 5b4ba3ac3f | |||
| ce259e4547 | |||
| fff1a54b6b | |||
| 9437581c48 | |||
| bd4db203f8 | |||
| 51387498a5 | |||
| 6d1336d8bd | |||
| 42931d6294 | |||
| 4c9e57777b | |||
| 210905ef66 | |||
| bd4de7b995 | |||
| 832f06e86e | |||
| a7ba3d73db | |||
| e2a15aadcc | |||
| e3f00c103e | |||
| a501202ab6 | |||
| 166cb323dd | |||
| b7585ebf71 | |||
| 3205239067 | |||
| c5b3066962 | |||
| 84d7777246 | |||
| 8412392788 | |||
| 7cc2e640b0 | |||
| e2fa3065f7 | |||
| 8787cbe85b | |||
| 226d501e9e | |||
| 6796ec67bd | |||
| 856e8e5205 | |||
| a7d6b5c69b | |||
| 6e18725906 | |||
| 7133d54f04 | |||
| 208bb20e98 | |||
| 6edc1b7822 | |||
| e754eae5ae | |||
| cc016f3b16 | |||
| 2188160418 | |||
| 77204d2e4a | |||
| 35eeb62f10 | |||
| 7dfa2b6e6c | |||
| a2eecec26f | |||
| 903e7ad79d | |||
| 13707fa2cf | |||
| b56e503b16 | |||
| 7d03685ade | |||
| b305c386bc | |||
| 240dd7d314 | |||
| b02ceacfd9 | |||
| fdf9007e2c | |||
| 08cee885d3 | |||
| 3bd5fdeb1b | |||
| e737c4a611 | |||
| 2eec4f3639 | |||
| e5912018f3 | |||
| b1f568d03d | |||
| e736707628 | |||
| d863f11f61 | |||
| 2ce873c65c | |||
| 964cf1e045 | |||
| eded9a98bb | |||
| a64fdbd23f | |||
| 36b9c5e197 | |||
| 851497abbd | |||
| 65a561e654 | |||
| 7b2c02a007 | |||
| 98f49eae96 | |||
| 1bdf44df9e | |||
| d59ecf22c1 | |||
| 22fa1a62fc | |||
| 3d48eed33a | |||
| 8f1c4a65dd | |||
| 9b97720f16 | |||
| 145e1d2d04 | |||
| 49403b6fab | |||
| 6d56f1dae8 | |||
| dd08a2aa89 | |||
| d9b47debb3 | |||
| 6cc8627ca8 | |||
| 54103dfd7d | |||
| 6012b9cc88 | |||
| 260b71cdd7 | |||
| 9ff468a6a7 | |||
| c83d114a73 | |||
| e964100dd9 | |||
| 6c0ae7a86a | |||
| 52ade637af | |||
| 569beebdd6 | |||
| 6cbf0f4e52 | |||
| 5615e6150f | |||
| 7465f37dcf | |||
| 38a1b90db3 | |||
| b53a5db60a | |||
| 372e03cef3 | |||
| ea8ad82786 | |||
| 6fffe252c0 | |||
| 86a84739e8 | |||
| 7f80100b3e | |||
| 61960961ba | |||
| cdd9ee042d | |||
| c742bfa1af | |||
| 0faea4ecaa | |||
| 7c9ec259a1 | |||
| 24a17709a8 | |||
| a3849af0cf | |||
| cc95e6fe05 | |||
| e7d9ed7917 | |||
| 876c6332f6 | |||
| 502d2448b2 | |||
| b9e48d9483 | |||
| b0554f8d0c | |||
| 12f3d95639 | |||
| 809fb29ca9 | |||
| be20bdf0be | |||
| 7c36b2a6a5 | |||
| 7979cb9dde | |||
| e563bbae7b | |||
| c63d1eec69 | |||
| 78d6d23359 | |||
| ec6781a231 | |||
| edf9030675 | |||
| 10e1c426a0 | |||
| dbab83ff82 | |||
| 478e66a93a | |||
| 3a5866eb4b | |||
| c0162bb3af | |||
| e9342a16f8 | |||
| 5cb86d8e51 | |||
| 66ed08f010 | |||
| b179166d28 | |||
| 4805526649 | |||
| 89159bf86e | |||
| b45b5d2c6e | |||
| 62866426c5 | |||
| 28a7db6129 | |||
| 7c5e81fd15 | |||
| 058d25d02c | |||
| fe09ec546f | |||
| cf611a4514 | |||
| 7cf30e969f | |||
| 18b71157db | |||
| 9904c490e4 | |||
| 32ed09128c | |||
| 81e96ee43c | |||
| 5f994589b3 | |||
| 40363194be | |||
| 21a5167b61 | |||
| 4d3957ca06 | |||
| 013c72fce7 | |||
| 40fadcb1f6 | |||
| 499a1ee478 | |||
| fa89eaf2f7 | |||
| 7964e20bc3 | |||
| 45c109ce88 | |||
| 74c08def06 | |||
| 907c8ab852 | |||
| 64945f3341 | |||
| 050d37c344 | |||
| f9f168fc1f | |||
| b1af3a3e45 | |||
| 0727296592 | |||
| 0e571b72fb | |||
| 366630f8c3 | |||
| 6bfafe0ec5 | |||
| 5e597f0a8b | |||
| 40f4d2f730 | |||
| b12ae7374d | |||
| 1afa6f5a58 | |||
| 977598d5ae | |||
| eb738aa61a | |||
| e9a030f917 | |||
| e6a9b217e6 | |||
| ec1743592c | |||
| fe647a11bb | |||
| 5a11b49b00 | |||
| 04692515bb | |||
| eb54c2a2e7 | |||
| a39c8eb0bd | |||
| 715555c2ab | |||
| 02b039f2ff | |||
| 00306414fe | |||
| db1179e057 | |||
| 7b0c9ca372 | |||
| e7255efdd6 |
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Skills shared by Baoyu for improving daily work efficiency",
|
||||
"version": "1.19.0"
|
||||
"version": "1.59.1"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
@@ -18,6 +18,7 @@
|
||||
"./skills/baoyu-xhs-images",
|
||||
"./skills/baoyu-post-to-x",
|
||||
"./skills/baoyu-post-to-wechat",
|
||||
"./skills/baoyu-post-to-weibo",
|
||||
"./skills/baoyu-article-illustrator",
|
||||
"./skills/baoyu-cover-image",
|
||||
"./skills/baoyu-slide-deck",
|
||||
@@ -43,7 +44,10 @@
|
||||
"skills": [
|
||||
"./skills/baoyu-danger-x-to-markdown",
|
||||
"./skills/baoyu-compress-image",
|
||||
"./skills/baoyu-url-to-markdown"
|
||||
"./skills/baoyu-url-to-markdown",
|
||||
"./skills/baoyu-format-markdown",
|
||||
"./skills/baoyu-markdown-to-html",
|
||||
"./skills/baoyu-translate"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -50,16 +50,16 @@ Just run `/release-skills` - auto-detects your project configuration.
|
||||
|
||||
**Language Detection Rules**:
|
||||
|
||||
| Filename Pattern | Language |
|
||||
|------------------|----------|
|
||||
| `CHANGELOG.md` (no suffix) | en (default) |
|
||||
| `CHANGELOG.zh.md` / `CHANGELOG_CN.md` / `CHANGELOG.zh-CN.md` | zh |
|
||||
| `CHANGELOG.ja.md` / `CHANGELOG_JP.md` | ja |
|
||||
| `CHANGELOG.ko.md` / `CHANGELOG_KR.md` | ko |
|
||||
| `CHANGELOG.de.md` / `CHANGELOG_DE.md` | de |
|
||||
| `CHANGELOG.fr.md` / `CHANGELOG_FR.md` | fr |
|
||||
| `CHANGELOG.es.md` / `CHANGELOG_ES.md` | es |
|
||||
| `CHANGELOG.{lang}.md` | Corresponding language code |
|
||||
Changelog files follow the pattern `CHANGELOG_{LANG}.md` or `CHANGELOG.{lang}.md`, where `{lang}` / `{LANG}` is a language or region code.
|
||||
|
||||
| Pattern | Example | Language |
|
||||
|---------|---------|----------|
|
||||
| No suffix | `CHANGELOG.md` | en (default) |
|
||||
| `_{LANG}` (uppercase) | `CHANGELOG_CN.md`, `CHANGELOG_JP.md` | Corresponding language |
|
||||
| `.{lang}` (lowercase) | `CHANGELOG.zh.md`, `CHANGELOG.ja.md` | Corresponding language |
|
||||
| `.{lang-region}` | `CHANGELOG.zh-CN.md` | Corresponding region variant |
|
||||
|
||||
Common language codes: `zh` (Chinese), `ja` (Japanese), `ko` (Korean), `de` (German), `fr` (French), `es` (Spanish).
|
||||
|
||||
**Output Example**:
|
||||
```
|
||||
@@ -114,11 +114,17 @@ Display version change: `1.2.3 → 1.3.0`
|
||||
For each detected changelog file:
|
||||
|
||||
1. **Identify language** from filename suffix
|
||||
2. **Generate content in that language**:
|
||||
2. **Detect third-party contributors**:
|
||||
- Check merge commits: `git log ${LAST_TAG}..HEAD --merges --pretty=format:"%H %s"`
|
||||
- For each merged PR, identify the PR author via `gh pr view <number> --json author --jq '.author.login'`
|
||||
- Compare against repo owner (`gh repo view --json owner --jq '.owner.login'`)
|
||||
- If PR author ≠ repo owner → third-party contributor
|
||||
3. **Generate content in that language**:
|
||||
- Section titles in target language
|
||||
- Change descriptions written naturally in target language (not translated)
|
||||
- Date format: YYYY-MM-DD (universal)
|
||||
3. **Insert at file head** (preserve existing content)
|
||||
- **Third-party contributions**: Append contributor attribution `(by @username)` to the changelog entry
|
||||
4. **Insert at file head** (preserve existing content)
|
||||
|
||||
**Section Title Translations** (built-in):
|
||||
|
||||
@@ -138,6 +144,7 @@ For each detected changelog file:
|
||||
|
||||
### Features
|
||||
- Description of new feature
|
||||
- Description of third-party contribution (by @username)
|
||||
|
||||
### Fixes
|
||||
- Description of fix
|
||||
@@ -148,6 +155,12 @@ For each detected changelog file:
|
||||
|
||||
Only include sections that have changes. Omit empty sections.
|
||||
|
||||
**Third-Party Attribution Rules**:
|
||||
- Only add `(by @username)` for contributors who are NOT the repo owner
|
||||
- Use GitHub username with `@` prefix
|
||||
- Place at the end of the changelog entry line
|
||||
- Apply to all languages consistently (always use `(by @username)` format, not translated)
|
||||
|
||||
**Multi-language Example**:
|
||||
|
||||
English (CHANGELOG.md):
|
||||
@@ -155,7 +168,7 @@ English (CHANGELOG.md):
|
||||
## 1.3.0 - 2026-01-22
|
||||
|
||||
### Features
|
||||
- Add user authentication module
|
||||
- Add user authentication module (by @contributor1)
|
||||
- Support OAuth2 login
|
||||
|
||||
### Fixes
|
||||
@@ -167,7 +180,7 @@ Chinese (CHANGELOG.zh.md):
|
||||
## 1.3.0 - 2026-01-22
|
||||
|
||||
### 新功能
|
||||
- 新增用户认证模块
|
||||
- 新增用户认证模块 (by @contributor1)
|
||||
- 支持 OAuth2 登录
|
||||
|
||||
### 修复
|
||||
@@ -179,25 +192,68 @@ Japanese (CHANGELOG.ja.md):
|
||||
## 1.3.0 - 2026-01-22
|
||||
|
||||
### 新機能
|
||||
- ユーザー認証モジュールを追加
|
||||
- ユーザー認証モジュールを追加 (by @contributor1)
|
||||
- OAuth2 ログインをサポート
|
||||
|
||||
### 修正
|
||||
- コネクションプールのメモリリークを修正
|
||||
```
|
||||
|
||||
### Step 5: Check README Updates
|
||||
### Step 5: Group Changes by Skill/Module
|
||||
|
||||
**IMPORTANT**: Before updating version file, check if README needs updates.
|
||||
Analyze commits since last tag and group by affected skill/module:
|
||||
|
||||
1. **Scan for README files**: `README*.md` in project root
|
||||
2. **For each skill/feature changed**, check if README documentation matches:
|
||||
- Options/flags documented correctly
|
||||
- Usage examples reflect current syntax
|
||||
- Feature descriptions are accurate
|
||||
3. **If README is outdated**:
|
||||
- Update README.md (and README.zh.md if exists) to reflect changes
|
||||
- Include in release commit
|
||||
1. **Identify changed files** per commit
|
||||
2. **Group by skill/module**:
|
||||
- `skills/<skill-name>/*` → Group under that skill
|
||||
- Root files (CLAUDE.md, etc.) → Group as "project"
|
||||
- Multiple skills in one commit → Split into multiple groups
|
||||
3. **For each group**, identify related README updates needed
|
||||
|
||||
**Example Grouping**:
|
||||
```
|
||||
baoyu-cover-image:
|
||||
- feat: add new style options
|
||||
- fix: handle transparent backgrounds
|
||||
→ README updates: options table
|
||||
|
||||
baoyu-comic:
|
||||
- refactor: improve panel layout algorithm
|
||||
→ No README updates needed
|
||||
|
||||
project:
|
||||
- docs: update CLAUDE.md architecture section
|
||||
```
|
||||
|
||||
### Step 6: Commit Each Skill/Module Separately
|
||||
|
||||
For each skill/module group (in order of changes):
|
||||
|
||||
1. **Check README updates needed**:
|
||||
- Scan `README*.md` for mentions of this skill/module
|
||||
- Verify options/flags documented correctly
|
||||
- Update usage examples if syntax changed
|
||||
- Update feature descriptions if behavior changed
|
||||
|
||||
2. **Stage and commit**:
|
||||
```bash
|
||||
git add skills/<skill-name>/*
|
||||
git add README.md README.zh.md # If updated for this skill
|
||||
git commit -m "<type>(<skill-name>): <meaningful description>"
|
||||
```
|
||||
|
||||
3. **Commit message format**:
|
||||
- Use conventional commit format: `<type>(<scope>): <description>`
|
||||
- `<type>`: feat, fix, refactor, docs, perf, etc.
|
||||
- `<scope>`: skill name or "project"
|
||||
- `<description>`: Clear, meaningful description of changes
|
||||
|
||||
**Example Commits**:
|
||||
```bash
|
||||
git commit -m "feat(baoyu-cover-image): add watercolor and minimalist styles"
|
||||
git commit -m "fix(baoyu-comic): improve panel layout for long dialogues"
|
||||
git commit -m "docs(project): update architecture documentation"
|
||||
```
|
||||
|
||||
**Common README Updates Needed**:
|
||||
| Change Type | README Section to Check |
|
||||
@@ -208,11 +264,13 @@ Japanese (CHANGELOG.ja.md):
|
||||
| Breaking changes | Migration notes, deprecation warnings |
|
||||
| Restructured internals | Architecture section (if exposed to users) |
|
||||
|
||||
### Step 6: Update Version File
|
||||
### Step 7: Generate Changelog and Update Version
|
||||
|
||||
1. Read version file (JSON/TOML/text)
|
||||
2. Update version number
|
||||
3. Write back (preserve formatting)
|
||||
1. **Generate multi-language changelogs** (as described in Step 4)
|
||||
2. **Update version file**:
|
||||
- Read version file (JSON/TOML/text)
|
||||
- Update version number
|
||||
- Write back (preserve formatting)
|
||||
|
||||
**Version Paths by File Type**:
|
||||
|
||||
@@ -224,25 +282,77 @@ Japanese (CHANGELOG.ja.md):
|
||||
| marketplace.json | `$.metadata.version` |
|
||||
| VERSION / version.txt | Direct content |
|
||||
|
||||
### Step 7: Commit and Tag
|
||||
### Step 8: User Confirmation
|
||||
|
||||
```bash
|
||||
git add <all modified files>
|
||||
git commit -m "chore: release v{VERSION}"
|
||||
git tag v{VERSION}
|
||||
Before creating the release commit, ask user to confirm:
|
||||
|
||||
**Use AskUserQuestion with two questions**:
|
||||
|
||||
1. **Version bump** (single select):
|
||||
- Show recommended version based on Step 3 analysis
|
||||
- Options: recommended (with label), other semver options
|
||||
- Example: `1.2.3 → 1.3.0 (Recommended)`, `1.2.3 → 1.2.4`, `1.2.3 → 2.0.0`
|
||||
|
||||
2. **Push to remote** (single select):
|
||||
- Options: "Yes, push after commit", "No, keep local only"
|
||||
|
||||
**Example Output Before Confirmation**:
|
||||
```
|
||||
Commits created:
|
||||
1. feat(baoyu-cover-image): add watercolor and minimalist styles
|
||||
2. fix(baoyu-comic): improve panel layout for long dialogues
|
||||
3. docs(project): update architecture documentation
|
||||
|
||||
Changelog preview (en):
|
||||
## 1.3.0 - 2026-01-22
|
||||
### Features
|
||||
- Add watercolor and minimalist styles to cover-image
|
||||
### Fixes
|
||||
- Improve panel layout for long dialogues in comic
|
||||
|
||||
Ready to create release commit and tag.
|
||||
```
|
||||
|
||||
### Step 9: Create Release Commit and Tag
|
||||
|
||||
After user confirmation:
|
||||
|
||||
1. **Stage version and changelog files**:
|
||||
```bash
|
||||
git add <version-file>
|
||||
git add CHANGELOG*.md
|
||||
```
|
||||
|
||||
2. **Create release commit**:
|
||||
```bash
|
||||
git commit -m "chore: release v{VERSION}"
|
||||
```
|
||||
|
||||
3. **Create tag**:
|
||||
```bash
|
||||
git tag v{VERSION}
|
||||
```
|
||||
|
||||
4. **Push if user confirmed** (Step 8):
|
||||
```bash
|
||||
git push origin main
|
||||
git push origin v{VERSION}
|
||||
```
|
||||
|
||||
**Note**: Do NOT add Co-Authored-By line. This is a release commit, not a code contribution.
|
||||
|
||||
**Important**: Do NOT push to remote. User will push manually when ready.
|
||||
|
||||
**Post-Release Output**:
|
||||
```
|
||||
Release v1.3.0 created locally.
|
||||
Release v1.3.0 created.
|
||||
|
||||
To publish:
|
||||
git push origin main
|
||||
git push origin v1.3.0
|
||||
Commits:
|
||||
1. feat(baoyu-cover-image): add watercolor and minimalist styles
|
||||
2. fix(baoyu-comic): improve panel layout for long dialogues
|
||||
3. docs(project): update architecture documentation
|
||||
4. chore: release v1.3.0
|
||||
|
||||
Tag: v1.3.0
|
||||
Status: Pushed to origin # or "Local only - run git push when ready"
|
||||
```
|
||||
|
||||
## Configuration (.releaserc.yml)
|
||||
@@ -307,31 +417,36 @@ Project detected:
|
||||
Last tag: v1.2.3
|
||||
Proposed version: v1.3.0
|
||||
|
||||
Changes detected:
|
||||
feat: add user authentication
|
||||
feat: support oauth2 login
|
||||
fix: memory leak in pool
|
||||
Changes grouped by skill/module:
|
||||
baoyu-cover-image:
|
||||
- feat: add watercolor style
|
||||
- feat: add minimalist style
|
||||
→ Commit: feat(baoyu-cover-image): add watercolor and minimalist styles
|
||||
→ README updates: options table
|
||||
|
||||
baoyu-comic:
|
||||
- fix: panel layout for long dialogues
|
||||
→ Commit: fix(baoyu-comic): improve panel layout for long dialogues
|
||||
→ No README updates
|
||||
|
||||
Changelog preview (en):
|
||||
## 1.3.0 - 2026-01-22
|
||||
### Features
|
||||
- Add user authentication module
|
||||
- Support OAuth2 login
|
||||
- Add watercolor and minimalist styles to cover-image
|
||||
### Fixes
|
||||
- Fix memory leak in connection pool
|
||||
- Improve panel layout for long dialogues in comic
|
||||
|
||||
Changelog preview (zh):
|
||||
## 1.3.0 - 2026-01-22
|
||||
### 新功能
|
||||
- 新增用户认证模块
|
||||
- 支持 OAuth2 登录
|
||||
- 为 cover-image 添加水彩和极简风格
|
||||
### 修复
|
||||
- 修复连接池内存泄漏问题
|
||||
- 改进 comic 长对话的面板布局
|
||||
|
||||
Files to modify:
|
||||
- package.json
|
||||
- CHANGELOG.md
|
||||
- CHANGELOG.zh.md
|
||||
Commits to create:
|
||||
1. feat(baoyu-cover-image): add watercolor and minimalist styles
|
||||
2. fix(baoyu-comic): improve panel layout for long dialogues
|
||||
3. chore: release v1.3.0
|
||||
|
||||
No changes made. Run without --dry-run to execute.
|
||||
```
|
||||
|
||||
+12
@@ -152,3 +152,15 @@ slide-deck/
|
||||
infographic/
|
||||
illustrations/
|
||||
comic/
|
||||
translate/
|
||||
posts/
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
.claude/skills/baoyu-skill-evolution
|
||||
# ClawHub local state (current and legacy directory names from the official CLI)
|
||||
.clawhub/
|
||||
.clawdhub/
|
||||
|
||||
+577
-3
@@ -2,6 +2,577 @@
|
||||
|
||||
English | [中文](./CHANGELOG.zh.md)
|
||||
|
||||
## 1.59.1 - 2026-03-11
|
||||
|
||||
### Fixes
|
||||
- `baoyu-translate`: improve short text annotation density rule and add explicit style preset passing to 02-prompt.md
|
||||
- `baoyu-post-to-x`: remove `--disable-blink-features=AutomationControlled` Chrome flag
|
||||
|
||||
### Refactor
|
||||
- `baoyu-post-to-weibo`: add entry point guard to md-to-html.ts for module import compatibility
|
||||
- Replace clawhub CLI with local sync-clawhub.mjs script
|
||||
|
||||
### Documentation
|
||||
- Update CLAUDE.md to reflect v1.59.0 codebase state (by @jackL1020)
|
||||
|
||||
## 1.59.0 - 2026-03-09
|
||||
|
||||
### Features
|
||||
- `baoyu-image-gen`: add batch parallel image generation and provider-level throttling (by @SeamoonAO)
|
||||
|
||||
### Fixes
|
||||
- `baoyu-image-gen`: restore Google as default provider when multiple keys available
|
||||
|
||||
### Documentation
|
||||
- Improve skill documentation clarity (by @SeamoonAO)
|
||||
|
||||
## 1.58.0 - 2026-03-08
|
||||
|
||||
### Features
|
||||
- Add XDG config path support for EXTEND.md (by @liby)
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-wechat`: surface agent-browser startup errors
|
||||
- `baoyu-post-to-wechat`: harden agent-browser command and eval handling (by @luojiyin1987)
|
||||
- `baoyu-image-gen`: use execFileSync for google curl requests (by @luojiyin1987)
|
||||
- `baoyu-format-markdown`: use spawnSync for autocorrect command (by @luojiyin1987)
|
||||
|
||||
### Documentation
|
||||
- Fix CLAUDE dependency statement (by @luojiyin1987)
|
||||
- Add markdown-to-html to README utility skills (by @luojiyin1987)
|
||||
|
||||
## 1.57.0 - 2026-03-08
|
||||
|
||||
### Features
|
||||
- Add ClawHub/OpenClaw publishing support with sync script and README documentation
|
||||
|
||||
### Refactor
|
||||
- Add openclaw metadata to all skill frontmatter for ClawHub registry compatibility
|
||||
- Rename `SKILL_DIR` to `baseDir` across all skills for consistency
|
||||
- `baoyu-danger-gemini-web`, `baoyu-danger-x-to-markdown`: dynamic script path in usage display
|
||||
- `baoyu-comic`, `baoyu-xhs-images`: use skill interface instead of direct script invocation for image generation
|
||||
|
||||
## 1.56.1 - 2026-03-08
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-weibo`: simplify article image insertion with Backspace-based placeholder deletion for ProseMirror compatibility
|
||||
|
||||
## 1.56.0 - 2026-03-08
|
||||
|
||||
### Features
|
||||
- `baoyu-article-illustrator`: preset-first selection flow with categorized style presets by content type
|
||||
- `baoyu-xhs-images`: streamline workflow from 6 to 4 steps with Smart Confirm (Quick/Customize/Detailed paths)
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-wechat`: improve image upload reliability with file chooser interception and fallback
|
||||
|
||||
## 1.55.0 - 2026-03-08
|
||||
|
||||
### Features
|
||||
- `baoyu-article-illustrator`: add screen-print style and `--preset` flag for quick type + style selection
|
||||
- `baoyu-cover-image`: add screen-print rendering and duotone palette with 5 new style presets
|
||||
- `baoyu-xhs-images`: add screen-print style and `--preset` flag with 23 built-in presets
|
||||
|
||||
### Documentation
|
||||
- Add credits section to both READMEs acknowledging open source inspirations
|
||||
|
||||
## 1.54.1 - 2026-03-07
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-x`: keep composed posts open in Chrome so users can review and publish manually
|
||||
|
||||
### Documentation
|
||||
- `baoyu-post-to-x`: document default post type selection and manual publishing flow
|
||||
- `README`: add Star History charts to the English and Chinese READMEs
|
||||
|
||||
## 1.54.0 - 2026-03-06
|
||||
|
||||
### Features
|
||||
- `baoyu-format-markdown`: improve title and summary generation with style-differentiated candidates, prohibited patterns, and hook-first principles
|
||||
- `baoyu-markdown-to-html`: add `--cite` option to convert ordinary external links to numbered bottom citations
|
||||
- `baoyu-post-to-wechat`: enable bottom citations by default for markdown input, add `--no-cite` flag to disable
|
||||
- `baoyu-translate`: support external glossary files via `glossary_files` in EXTEND.md (markdown table or YAML)
|
||||
- `baoyu-translate`: add frontmatter transformation rules to rename source metadata fields with `source` prefix
|
||||
|
||||
## 1.53.0 - 2026-03-06
|
||||
|
||||
### Features
|
||||
- `baoyu-url-to-markdown`: save rendered HTML snapshot as `-captured.html` alongside markdown output
|
||||
- `baoyu-url-to-markdown`: Defuddle-first markdown conversion with automatic fallback to legacy Readability/selector extractor
|
||||
|
||||
## 1.52.0 - 2026-03-06
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-weibo`: add video upload support via `--video` flag (max 18 files total)
|
||||
- `baoyu-post-to-weibo`: switch from clipboard paste to `DOM.setFileInputFiles` for more reliable uploads
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-weibo`: add Chrome health check with auto-restart for unresponsive instances
|
||||
- `baoyu-post-to-weibo`: add navigation check to ensure Weibo home page before posting
|
||||
|
||||
## 1.51.2 - 2026-03-06
|
||||
|
||||
### Fixes
|
||||
- `release-skills`: replace explicit language filename patterns (e.g. `CHANGELOG.de.md`) with generic pattern to avoid Gen Agent Trust Hub URL scanner false positive
|
||||
- `baoyu-infographic`: add credential/secret stripping instructions to address Snyk W007 insecure credential handling audit
|
||||
|
||||
## 1.51.1 - 2026-03-06
|
||||
|
||||
### Refactor
|
||||
- Unify Chrome CDP profile path — all skills now share `baoyu-skills/chrome-profile` instead of per-skill directories
|
||||
- Fix `baoyu-post-to-weibo` incorrectly reusing `x-browser-profile` path
|
||||
|
||||
### Fixes
|
||||
- Remove `curl | bash` remote code execution pattern from all install instructions
|
||||
- Enforce HTTPS-only for remote image downloads in `md-to-html` scripts
|
||||
- Add redirect limit (max 5) to prevent infinite redirect loops
|
||||
- Add Security Guidelines section to CLAUDE.md
|
||||
|
||||
## 1.51.0 - 2026-03-06
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-weibo`: new skill for posting to Weibo — supports text posts with images and headline articles (头条文章) via Chrome CDP
|
||||
- `baoyu-format-markdown`: add title/summary multi-candidate selection — generates 3 candidates for user to pick, with `auto_select` EXTEND.md support
|
||||
|
||||
## 1.50.0 - 2026-03-06
|
||||
|
||||
### Features
|
||||
- `baoyu-translate`: expand translation style presets from 4 to 9 — add academic, business, humorous, conversational, and elegant styles
|
||||
- `baoyu-translate`: add `--style` CLI flag for per-invocation style override
|
||||
- `baoyu-translate`: integrate style instructions into subagent prompt template
|
||||
|
||||
## 1.49.0 - 2026-03-06
|
||||
|
||||
### Features
|
||||
- `baoyu-format-markdown`: add reader-perspective content analysis phase — analyzes highlights, structure, and formatting issues before applying formatting
|
||||
- `baoyu-format-markdown`: restructure workflow from 8 steps to 7 with explicit do/don't formatting principles and completion report
|
||||
- `baoyu-translate`: extract Step 2 workflow mechanics to separate reference file for cleaner SKILL.md
|
||||
- `baoyu-translate`: expand trigger keywords (改成中文, 快翻, 本地化, etc.) for better skill activation
|
||||
- `baoyu-translate`: add proactive warning for long content in quick mode
|
||||
- `baoyu-translate`: save frontmatter to `chunks/frontmatter.md` during chunking
|
||||
|
||||
## 1.48.2 - 2026-03-06
|
||||
|
||||
### Features
|
||||
- `baoyu-translate`: add figurative language & emotional fidelity review steps to refined workflow critique and revision stages
|
||||
- `baoyu-translate`: enhance quick mode to enforce meaning-first translation principles for figurative language
|
||||
|
||||
## 1.48.1 - 2026-03-05
|
||||
|
||||
### Features
|
||||
- `baoyu-translate`: add figurative language & metaphor mapping to analysis step — interprets metaphors, idioms, and implied meanings before translation instead of translating literally
|
||||
- `baoyu-translate`: add "meaning over words", "figurative language", and "emotional fidelity" translation principles to SKILL.md, refined workflow, and subagent prompt template
|
||||
|
||||
## 1.48.0 - 2026-03-05
|
||||
|
||||
### Features
|
||||
- `baoyu-translate`: add `--output-dir` option to chunk.ts — chunks now write to the translation output directory instead of the source file directory
|
||||
- `baoyu-translate`: improve refined workflow — split Review into Critical Review + Revision (5→6 steps), add Europeanized language diagnosis for CJK targets
|
||||
|
||||
## 1.47.0 - 2026-03-05
|
||||
|
||||
### Features
|
||||
- Add `baoyu-translate` skill — three-mode translation (quick/normal/refined) with custom glossaries, audience-aware translation, and parallel chunked translation for long documents
|
||||
- Add cross-platform PowerShell support for EXTEND.md preference checks across all skills
|
||||
|
||||
## 1.46.0 - 2026-03-05
|
||||
|
||||
### Features
|
||||
- Add `--output-dir` option to url-to-markdown for custom output directory with auto-generated filenames
|
||||
|
||||
## 1.45.1 - 2026-03-05
|
||||
|
||||
### Refactor
|
||||
- Replace hardcoded `npx -y bun` with `${BUN_X}` runtime variable across all skills — prefers native `bun`, falls back to `npx -y bun`
|
||||
- Add Runtime Detection section to CLAUDE.md and Script Directory instructions in all SKILL.md files
|
||||
|
||||
## 1.45.0 - 2026-03-05
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-x`: add post-composition verification for X Articles — automatically checks remaining placeholders and image count after all images are inserted
|
||||
- `baoyu-post-to-x`: increase CDP timeout to 60s and add 3s DOM stabilization delay between image insertions for long articles
|
||||
|
||||
## 1.44.0 - 2026-03-05
|
||||
|
||||
### Features
|
||||
- `baoyu-url-to-markdown`: add `--download-media` flag to download images and videos to local directories, rewriting markdown links to local paths
|
||||
- `baoyu-url-to-markdown`: extract cover image from page meta (og:image) into YAML front matter `coverImage` field
|
||||
- `baoyu-url-to-markdown`: handle `data-src` lazy loading for WeChat and similar sites
|
||||
- `baoyu-url-to-markdown`: add EXTEND.md preferences with first-time setup for media download behavior
|
||||
|
||||
## 1.43.2 - 2026-03-05
|
||||
|
||||
### Refactor
|
||||
- `baoyu-url-to-markdown`: replace custom HTML extraction (linkedom + Readability + Turndown) with defuddle library for cleaner content extraction and markdown conversion
|
||||
|
||||
## 1.43.1 - 2026-03-02
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-x`: auto-detect WSL environment and resolve Chrome profile to Windows-native path for stable login persistence
|
||||
- `baoyu-post-to-wechat`: auto-detect WSL environment and resolve Chrome profile to Windows-native path for stable login persistence
|
||||
- `baoyu-danger-gemini-web`: WSL auto-detection for Chrome profile path; add `GEMINI_WEB_DEBUG_PORT` env var for fixed debug port
|
||||
- `baoyu-danger-x-to-markdown`: WSL auto-detection for Chrome profile path; add `X_DEBUG_PORT` env var for fixed debug port
|
||||
|
||||
## 1.43.0 - 2026-03-02
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-wechat`: support env var overrides for browser debug port (`WECHAT_BROWSER_DEBUG_PORT`) and profile directory (`WECHAT_BROWSER_PROFILE_DIR`)
|
||||
- `baoyu-post-to-x`: support env var overrides for browser debug port (`X_BROWSER_DEBUG_PORT`) and profile directory (`X_BROWSER_PROFILE_DIR`)
|
||||
|
||||
## 1.42.3 - 2026-03-02
|
||||
|
||||
### Fixes
|
||||
- `baoyu-image-gen`: use standard size presets for DashScope aspect ratio mapping instead of free-form calculation
|
||||
|
||||
## 1.42.2 - 2026-03-01
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: inline rendering pipeline (no subprocess), fix CJK emphasis order, enhance modern theme with GFM alerts and improved typography
|
||||
- `baoyu-post-to-wechat`: internalize markdown conversion with modular renderer, add color support, simplify publishing workflow
|
||||
|
||||
## 1.42.1 - 2026-02-28
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: modularize render.ts into cli, constants, extend-config, html-builder, renderer, themes, and types modules; bundle code highlighting themes locally
|
||||
|
||||
## 1.42.0 - 2026-02-28
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: consolidate heritage and warm into single modern theme, add per-theme color defaults (default→blue, grace→purple, simple→green, modern→orange)
|
||||
- `baoyu-post-to-wechat`: add default color preference support in EXTEND.md, add modern theme option to first-time setup
|
||||
|
||||
## 1.41.0 - 2026-02-28
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: rename themes (red→heritage, orange→warm), add 13 named color presets, serif-cjk font family, and per-theme style defaults
|
||||
|
||||
## 1.40.1 - 2026-02-28
|
||||
|
||||
### Features
|
||||
- `baoyu-image-gen`: clarify model resolution priority (EXTEND.md overrides env vars) and display current model with switch hints during generation
|
||||
|
||||
## 1.40.0 - 2026-02-28
|
||||
|
||||
### Features
|
||||
- `baoyu-image-gen`: support OpenAI chat completions endpoint for image generation (by @zhao-newname)
|
||||
- `baoyu-markdown-to-html`: add CLI customization options (--color, --font-family, --font-size, --code-theme, --mac-code-block, --line-number, --cite, --count, --legend) and EXTEND.md config support
|
||||
|
||||
## 1.39.0 - 2026-02-28
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: add red theme (traditional calligraphy style with red-gold palette and serif typography) and orange theme (warm modern style with rounded corners and relaxed line height)
|
||||
|
||||
## 1.38.0 - 2026-02-28
|
||||
|
||||
### Features
|
||||
- `baoyu-danger-x-to-markdown`: render embedded tweets in articles as blockquotes with author info and text summary
|
||||
- `baoyu-danger-x-to-markdown`: reuse existing markdown when `--download-media` targets already-converted URLs
|
||||
- `baoyu-danger-x-to-markdown`: upgrade Twitter image downloads to 4096x4096 high resolution
|
||||
|
||||
### Fixes
|
||||
- `baoyu-danger-x-to-markdown`: improve entity resolution with logical key lookup for reliable media and link mapping
|
||||
- `baoyu-danger-x-to-markdown`: support trailing media for all block types (headings, lists, blockquotes)
|
||||
|
||||
## 1.37.1 - 2026-02-27
|
||||
|
||||
### Fixes
|
||||
- `baoyu-danger-gemini-web`: sync model headers with upstream and update model list (by @xkcoding)
|
||||
|
||||
## 1.37.0 - 2026-02-27
|
||||
|
||||
### Features
|
||||
- `baoyu-danger-x-to-markdown`: add inline link rendering for X article content, mapping LINK/MEDIA entities to markdown links
|
||||
- `baoyu-danger-x-to-markdown`: use content-based slug in output directory path for meaningful folder names
|
||||
- `baoyu-danger-x-to-markdown`: add atomic media queue for blocks without direct media references
|
||||
|
||||
## 1.36.0 - 2026-02-27
|
||||
|
||||
### Features
|
||||
- `baoyu-image-gen`: add `gemini-3.1-flash-image-preview` model support for Google multimodal image generation
|
||||
- `baoyu-image-gen`: improve first-time setup with blocking preferences flow and guided configuration
|
||||
|
||||
### Fixes
|
||||
- `baoyu-image-gen`: use curl fallback for Google API when HTTP proxy is detected (by @liye71023326)
|
||||
|
||||
## 1.35.0 - 2026-02-24
|
||||
|
||||
### Features
|
||||
- `baoyu-image-gen`: add Replicate provider support with configurable models (by @justnode)
|
||||
- `baoyu-infographic`: add `dense-modules` layout and 3 new styles (`morandi-journal`, `pop-laboratory`, `retro-pop-grid`) for high-density infographics. Add keyword shortcuts for auto-selection. Prompt credit: [AJ](https://waytoagi.feishu.cn/wiki/YG0zwalijihRREkgmPzcWRInnUg)
|
||||
|
||||
### Documentation
|
||||
- `baoyu-image-gen`: add Replicate model configuration documentation
|
||||
|
||||
## 1.34.2 - 2026-02-25
|
||||
|
||||
### Documentation
|
||||
- `baoyu-markdown-to-html`: clarify theme resolution order with local and cross-skill EXTEND.md fallbacks before prompting user.
|
||||
- `baoyu-post-to-wechat`: align markdown conversion theme handling with deterministic fallback (`CLI --theme` -> EXTEND.md `default_theme` -> `default`) and require explicit `--theme` parameter.
|
||||
|
||||
## 1.34.1 - 2026-02-20
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-wechat`: fix upload progress check crashing on second iteration (by @LyInfi)
|
||||
|
||||
## 1.34.0 - 2026-02-17
|
||||
|
||||
### Features
|
||||
- `baoyu-xhs-images`: add reference image chain for visual consistency across multi-image series (by @jeffrey94)
|
||||
|
||||
### Refactor
|
||||
- `baoyu-article-illustrator`: enforce prompt file creation as blocking step before image generation, add structured prompt quality requirements (ZONES / LABELS / COLORS / STYLE / ASPECT) and verification checklist.
|
||||
|
||||
## 1.33.1 - 2026-02-14
|
||||
|
||||
### Refactor
|
||||
- `baoyu-post-to-x`: replace hand-rolled markdown parser with marked ecosystem for X Articles HTML conversion.
|
||||
|
||||
### Documentation
|
||||
- `baoyu-post-to-x`: remove `--submit` flag from all scripts; clarify that scripts only fill content into browser for manual review and publish.
|
||||
|
||||
## 1.33.0 - 2026-02-13
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-x`: add pre-flight environment check script (`check-paste-permissions.ts`); add troubleshooting section for Chrome debug port conflicts; replace fixed sleep with image upload verification polling up to 15s.
|
||||
- `baoyu-post-to-wechat`: add pre-flight environment check script (`check-permissions.ts`) covering Chrome, profile isolation, Bun, Accessibility, clipboard, paste keystroke, API credentials.
|
||||
|
||||
## 1.32.0 - 2026-02-12
|
||||
|
||||
### Features
|
||||
- `baoyu-danger-x-to-markdown`: add `--download-media` flag to download images/videos locally and rewrite markdown links to relative paths; add media localization module; add first-time setup with EXTEND.md preferences; add `coverImage` to frontmatter output.
|
||||
|
||||
### Refactor
|
||||
- `baoyu-danger-x-to-markdown`: use camelCase for frontmatter keys (`tweetCount`, `coverImage`, `requestedUrl`, etc.).
|
||||
- `baoyu-format-markdown`: rename `featureImage` to `coverImage` as primary frontmatter key (with `featureImage` as accepted alias).
|
||||
- `baoyu-post-to-wechat`: prioritize `coverImage` over `featureImage` in cover image frontmatter lookup order.
|
||||
|
||||
## 1.31.2 - 2026-02-10
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-wechat`: fix PowerShell clipboard copy failing on Windows due to `param()`/`-Path` not working with `-Command`.
|
||||
- `baoyu-post-to-x`: fix PowerShell clipboard copy on Windows (same issue); fix `getScriptDir()` returning invalid path on Windows (`/C:/...` prefix).
|
||||
|
||||
## 1.31.1 - 2026-02-10
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-wechat`: adapt to new WeChat UI — rename 图文 to 贴图; add ProseMirror editor support with old editor fallback; add fallback file input selector; add upload progress monitoring; improve save button detection with toast verification.
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-wechat`: truncate digest > 120 chars at punctuation boundary; fix cover image relative path resolution.
|
||||
- `baoyu-post-to-x`: fix Chrome launch on macOS via `open -na`; fix cover image relative path resolution.
|
||||
|
||||
## 1.31.0 - 2026-02-07
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-wechat`: add comment control settings (`need_open_comment`, `only_fans_can_comment`); add cover image fallback chain (CLI → frontmatter → `imgs/cover.png` → first inline image); add author resolution priority; add first-time setup flow with EXTEND.md preferences.
|
||||
|
||||
## 1.30.3 - 2026-02-06
|
||||
|
||||
### Refactor
|
||||
- `baoyu-article-illustrator`: optimize SKILL.md from 197 to 150 lines (24% reduction); apply progressive disclosure pattern with concise overview and detailed references.
|
||||
|
||||
## 1.30.2 - 2026-02-06
|
||||
|
||||
### Refactor
|
||||
- `baoyu-cover-image`: optimize SKILL.md from 532 to 233 lines (56% reduction); extract reference image handling to `references/workflow/reference-images.md`; condense galleries to value-only tables with links.
|
||||
|
||||
## 1.30.1 - 2026-02-06
|
||||
|
||||
### Features
|
||||
- `baoyu-image-gen`: add OpenAI GPT Image edits support for reference images (`--ref`); auto-select Google or OpenAI when ref provided.
|
||||
|
||||
### Fixes
|
||||
- `baoyu-image-gen`: change ref-related warnings to explicit errors with fix hints; add reference image validation.
|
||||
- `baoyu-cover-image`: enhance reference image analysis with deep extraction template; require MUST INCORPORATE section for concrete visual elements.
|
||||
|
||||
## 1.30.0 - 2026-02-06
|
||||
|
||||
### Features
|
||||
- `baoyu-cover-image`: add font dimension with 4 typography styles (clean, handwritten, serif, display); includes auto-selection rules, compatibility matrix, and `warm-flat` style preset.
|
||||
|
||||
## 1.29.0 - 2026-02-06
|
||||
|
||||
### Features
|
||||
- `baoyu-image-gen`: add EXTEND.md configuration support, including schema documentation and runtime preference loading in scripts (by @kingdomad).
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-wechat`: fix duplicated title and ordered-list numbering in WeChat article publishing (by @NantesCheval).
|
||||
- `baoyu-url-to-markdown`: replace regex-only conversion with multi-strategy content extraction and Turndown conversion; improve noise filtering for Substack-style pages.
|
||||
|
||||
## 1.28.4 - 2026-02-03
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: add author and description meta tags to generated HTML from YAML frontmatter; strip quotes from frontmatter values (supports both English and Chinese quotation marks).
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-wechat`: remove extra empty lines after image paste; fix summary field timing to fill after content paste (prevents being overwritten).
|
||||
|
||||
## 1.28.3 - 2026-02-03
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-wechat`: fix placeholder matching issue where `WECHATIMGPH_1` incorrectly matched `WECHATIMGPH_10`.
|
||||
|
||||
## 1.28.2 - 2026-02-03
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-x`: reuse existing Chrome instance when available; fix placeholder matching issue where `XIMGPH_1` incorrectly matched `XIMGPH_10`; improve image sorting by placeholder index; use `execCommand` for more reliable placeholder deletion.
|
||||
|
||||
## 1.28.1 - 2026-02-02
|
||||
|
||||
### Refactor
|
||||
- `baoyu-article-illustrator`: simplify main SKILL.md by extracting detailed procedures to `workflow.md`; add Core Styles tier (vector, minimal-flat, sci-fi, hand-drawn, editorial, scene) for quick selection; add `vector-illustration` as recommended default style; add Illustration Purpose (information/visualization/imagination) for better type/style recommendations; add default composition requirements, character rendering guidelines, and text styling rules to prompt construction.
|
||||
|
||||
## 1.28.0 - 2026-02-01
|
||||
|
||||
### Features
|
||||
- `baoyu-cover-image`: add reference image support (`--ref` parameter) with direct/style/palette usage types; add visual elements library with icon vocabulary by topic.
|
||||
- `baoyu-article-illustrator`: add reference image support with direct/style/palette usage types.
|
||||
- `baoyu-post-to-wechat`: add `newspic` article type for image-text posts.
|
||||
|
||||
### Refactor
|
||||
- `baoyu-cover-image`, `baoyu-article-illustrator`, `baoyu-comic`, `baoyu-xhs-images`: enforce first-time setup as blocking operation before any other workflow steps.
|
||||
- `baoyu-cover-image`: remove character limits from titles, use original source titles.
|
||||
|
||||
## 1.26.1 - 2026-01-29
|
||||
|
||||
### Features
|
||||
- `baoyu-article-illustrator`, `baoyu-comic`, `baoyu-cover-image`, `baoyu-infographic`, `baoyu-slide-deck`, `baoyu-xhs-images`: add backup rules for existing files—automatically renames source, prompt, and image files with timestamp suffix before overwriting.
|
||||
|
||||
### Fixes
|
||||
- `baoyu-xhs-images`: remove `notebook` style (10 styles remaining).
|
||||
|
||||
## 1.26.0 - 2026-01-29
|
||||
|
||||
### Features
|
||||
- `baoyu-xhs-images`: add `notebook` style (hand-drawn infographic with watercolor rendering and Morandi palette) and `study-notes` style (realistic handwritten photo aesthetic).
|
||||
- `baoyu-xhs-images`: add `mindmap` (center radial) and `quadrant` (four-section grid) layouts.
|
||||
|
||||
## 1.25.4 - 2026-01-29
|
||||
|
||||
### Fixes
|
||||
- `baoyu-markdown-to-html`: generate proper `<img>` tags with `data-local-path` attribute instead of text placeholders.
|
||||
- `baoyu-post-to-wechat`: fix API publishing to read image paths from `data-local-path` attribute; fix title/cover extraction from corresponding `.md` frontmatter when publishing HTML files.
|
||||
- `baoyu-post-to-wechat`: fix CLI argument parsing to handle unknown parameters gracefully; add `--summary` parameter support.
|
||||
- `baoyu-post-to-wechat`: fix browser publishing to convert `<img>` tags back to text placeholders before paste.
|
||||
|
||||
## 1.25.3 - 2026-01-28
|
||||
|
||||
### Features
|
||||
- `baoyu-format-markdown`: add content type detection with user confirmation for markdown files; add CJK punctuation handling to move paired punctuation outside emphasis markers.
|
||||
|
||||
## 1.25.2 - 2026-01-28
|
||||
|
||||
### Documentation
|
||||
- `baoyu-post-to-wechat`: add WeChat API credentials configuration guide to README.
|
||||
|
||||
## 1.25.1 - 2026-01-28
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: add pre-check step for CJK content to suggest formatting with `baoyu-format-markdown` before conversion.
|
||||
|
||||
## 1.25.0 - 2026-01-28
|
||||
|
||||
### Features
|
||||
- `baoyu-format-markdown`: add markdown formatter skill with frontmatter, typography, and CJK spacing support.
|
||||
- `baoyu-markdown-to-html`: add markdown to HTML converter with WeChat-compatible themes, code highlighting, math, PlantUML, and alerts.
|
||||
- `baoyu-post-to-wechat`: add API-based publishing method and external theme support.
|
||||
|
||||
## 1.24.4 - 2026-01-28
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-x`: fix Apply button click for cover image modal; add retry logic and wait for modal close.
|
||||
|
||||
## 1.24.3 - 2026-01-28
|
||||
|
||||
### Documentation
|
||||
- Emphasize updating prompt files before regenerating images in modification workflows (article-illustrator, slide-deck, xhs-images, cover-image, comic).
|
||||
|
||||
## 1.24.2 - 2026-01-28
|
||||
|
||||
### Refactor
|
||||
- `baoyu-image-gen`: default to sequential generation; parallel available on request.
|
||||
|
||||
## 1.24.1 - 2026-01-28
|
||||
|
||||
### Features
|
||||
- `baoyu-image-gen`: add Aliyun Tongyi Wanxiang (DashScope) text-to-image model support (by @JianJang2017).
|
||||
|
||||
### Documentation
|
||||
- Add Aliyun text-to-image model configuration to README.
|
||||
|
||||
## 1.24.0 - 2026-01-27
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-wechat`: reuse existing Chrome browser instead of requiring all windows closed (by @AliceLJY).
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-wechat`: improves title extraction to support h1/h2 headings; adds summary auto-fill and content verification after paste/type; supports flexible HTML meta tag attribute ordering.
|
||||
|
||||
### Documentation
|
||||
- `release-skills`: adds third-party contributor attribution rules to changelog workflow.
|
||||
- Backfills missing third-party contributor attributions across historical changelog entries.
|
||||
|
||||
## 1.23.1 - 2026-01-27
|
||||
|
||||
### Fixes
|
||||
- `baoyu-compress-image`: rename original file as `_original` backup instead of deleting after compression.
|
||||
|
||||
## 1.23.0 - 2026-01-26
|
||||
|
||||
### Refactor
|
||||
- `baoyu-cover-image`: replaces 20 fixed styles with 5-dimension system (Type × Palette × Rendering × Text × Mood). 9 color palettes × 6 rendering styles = 54 combinations. Adds style presets for backward compatibility, v2→v3 schema migration, and new reference structure (`palettes/`, `renderings/`, `workflow/`).
|
||||
|
||||
## 1.22.0 - 2026-01-25
|
||||
|
||||
### Features
|
||||
- `baoyu-article-illustrator`: adds `imgs-subdir` output directory option; improves style selection to always ask and show preferred_style from EXTEND.md.
|
||||
- `baoyu-cover-image`: adds `default_output_dir` preference supporting `same-dir`, `imgs-subdir`, and `independent` options with Step 1.5 for output directory selection.
|
||||
- `baoyu-post-to-wechat`: adds theme selection (default/grace/simple) with AskUserQuestion before posting; adds HTML preview step; simplifies image placeholders to `WECHATIMGPH_N` format; refactors copy/paste to cross-platform helpers.
|
||||
|
||||
### Refactor
|
||||
- `baoyu-post-to-x`: simplifies image placeholders from `[[IMAGE_PLACEHOLDER_N]]` to `XIMGPH_N` format.
|
||||
|
||||
## 1.21.4 - 2026-01-25
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-wechat`: adds Windows compatibility—uses `fileURLToPath` for correct path resolution, replaces system-dependent copy/paste tools (osascript/xdotool) with CDP keyboard events for cross-platform support (by @JadeLiang003).
|
||||
- `baoyu-post-to-wechat`: fixes regressions from Windows compatibility PR—corrects broken `-fixed` filename references, restores frontmatter quote stripping, restores `--title` CLI parameter, fixes summary extraction to skip headings/quotes/lists, fixes argument parsing for single-dash flags, removes debug logs.
|
||||
- `baoyu-article-illustrator`, `baoyu-cover-image`, `baoyu-xhs-images`: removes opacity option from watermark configuration.
|
||||
|
||||
## 1.21.3 - 2026-01-24
|
||||
|
||||
### Refactor
|
||||
- `baoyu-article-illustrator`: simplifies SKILL.md by extracting content to reference files—adds `references/usage.md` for command syntax, `references/prompt-construction.md` for prompt templates. Reorganizes workflow from 5 to 6 steps with new Pre-check phase. Adds `default_output_dir` preference option.
|
||||
|
||||
## 1.21.2 - 2026-01-24
|
||||
|
||||
### Features
|
||||
- `baoyu-image-gen`: adds parallel generation documentation with recommended 4 concurrent subagents for batch operations.
|
||||
|
||||
### Documentation
|
||||
- `release-skills`: adds skill/module grouping workflow and user confirmation step before release.
|
||||
|
||||
## 1.21.1 - 2026-01-24
|
||||
|
||||
### Documentation
|
||||
- `baoyu-comic`: adds character sheet compression step after generation to reduce token usage when used as reference image.
|
||||
|
||||
## 1.21.0 - 2026-01-24
|
||||
|
||||
### Features
|
||||
- `baoyu-cover-image`: expands aspect ratio options—adds 4:3, 3:2, 3:4 ratios; changes default from 2.35:1 to 16:9 for better versatility. Aspect ratio is now always confirmed unless explicitly specified via `--aspect` flag.
|
||||
- `baoyu-image-gen`: refactors Google provider to support both Gemini multimodal and Imagen models with unified API. Adds `--imageSize` parameter support (1K/2K/4K) for Gemini models.
|
||||
|
||||
## 1.20.0 - 2026-01-24
|
||||
|
||||
### Features
|
||||
- `baoyu-cover-image`: upgrades from Type × Style two-dimension system to **4-dimension system**—adds `--text` dimension (none, title-only, title-subtitle, text-rich) for text density control and `--mood` dimension (subtle, balanced, bold) for emotional intensity. New `--quick` flag skips confirmation and uses auto-selection.
|
||||
|
||||
### Documentation
|
||||
- `baoyu-cover-image`: adds dimension reference files—`references/dimensions/text.md` (text density levels) and `references/dimensions/mood.md` (mood intensity levels).
|
||||
- `baoyu-cover-image`: updates base-prompt, first-time-setup, and preferences-schema to support new 4-dimension system with v2 schema.
|
||||
- `README.md`, `README.zh.md`: updates baoyu-cover-image documentation to reflect new 4-dimension system with `--text`, `--mood`, and `--quick` options.
|
||||
|
||||
## 1.19.0 - 2026-01-24
|
||||
|
||||
### Features
|
||||
@@ -114,7 +685,7 @@ English | [中文](./CHANGELOG.zh.md)
|
||||
## 1.14.0 - 2026-01-22
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-x`: improves video ready detection for more reliable video posting.
|
||||
- `baoyu-post-to-x`: improves video ready detection for more reliable video posting (by @fkysly).
|
||||
|
||||
### Documentation
|
||||
- `baoyu-slide-deck`: comprehensive SKILL.md enhancement—adds slide count guidance (recommended 8-25, max 30), audience guidelines table with audience-specific principles, style selection principles with content-type recommendations, layout selection tips with common mistakes to avoid, visual hierarchy principles, content density guidelines (McKinsey-style high-density principles), color selection guide, typography principles with font recommendations (English and Chinese fonts with multilingual pairing), and visual elements reference (backgrounds, typography treatments, geometric accents).
|
||||
@@ -129,6 +700,9 @@ English | [中文](./CHANGELOG.zh.md)
|
||||
|
||||
## 1.12.0 - 2026-01-21
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-x`: adds quote tweet support (by @threehotpot-bot).
|
||||
|
||||
### Refactor
|
||||
- `baoyu-post-to-x`: extracts shared utilities to `x-utils.ts`—consolidates Chrome detection, CDP connection, clipboard operations, and helper functions from `x-article.ts`, `x-browser.ts`, `x-quote.ts`, and `x-video.ts` into a single reusable module.
|
||||
|
||||
@@ -144,7 +718,7 @@ English | [中文](./CHANGELOG.zh.md)
|
||||
## 1.10.0 - 2026-01-21
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-x`: adds video posting support—new `x-video.ts` script for posting text with video files (MP4, MOV, WebM). Supports preview mode and handles video processing timeouts.
|
||||
- `baoyu-post-to-x`: adds video posting support—new `x-video.ts` script for posting text with video files (MP4, MOV, WebM). Supports preview mode and handles video processing timeouts (by @fkysly).
|
||||
|
||||
## 1.9.0 - 2026-01-20
|
||||
|
||||
@@ -200,7 +774,7 @@ English | [中文](./CHANGELOG.zh.md)
|
||||
## 1.4.1 - 2026-01-18
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-x`: supports multi-language UI selectors for X Articles (contributed by [@ianchenx](https://github.com/ianchenx)).
|
||||
- `baoyu-post-to-x`: supports multi-language UI selectors for X Articles (by @ianchenx).
|
||||
|
||||
## 1.4.0 - 2026-01-18
|
||||
|
||||
|
||||
+577
-3
@@ -2,6 +2,577 @@
|
||||
|
||||
[English](./CHANGELOG.md) | 中文
|
||||
|
||||
## 1.59.1 - 2026-03-11
|
||||
|
||||
### 修复
|
||||
- `baoyu-translate`:改进短文本注释密度规则,补充风格预设到 02-prompt.md 的显式传递
|
||||
- `baoyu-post-to-x`:移除 `--disable-blink-features=AutomationControlled` Chrome 启动参数
|
||||
|
||||
### 重构
|
||||
- `baoyu-post-to-weibo`:为 md-to-html.ts 添加入口守卫,支持模块导入
|
||||
- 使用本地 sync-clawhub.mjs 脚本替代 clawhub CLI
|
||||
|
||||
### 文档
|
||||
- 更新 CLAUDE.md 以反映 v1.59.0 代码库状态 (by @jackL1020)
|
||||
|
||||
## 1.59.0 - 2026-03-09
|
||||
|
||||
### 新功能
|
||||
- `baoyu-image-gen`:新增批量并行图片生成和提供商级别限流 (by @SeamoonAO)
|
||||
|
||||
### 修复
|
||||
- `baoyu-image-gen`:修复多个 API key 可用时恢复 Google 为默认提供商
|
||||
|
||||
### 文档
|
||||
- 改进技能文档清晰度 (by @SeamoonAO)
|
||||
|
||||
## 1.58.0 - 2026-03-08
|
||||
|
||||
### 新功能
|
||||
- 新增 EXTEND.md 的 XDG 配置路径支持 (by @liby)
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-wechat`:暴露 agent-browser 启动错误信息
|
||||
- `baoyu-post-to-wechat`:加固 agent-browser 命令和 eval 处理 (by @luojiyin1987)
|
||||
- `baoyu-image-gen`:使用 execFileSync 替代 shell 执行 Google curl 请求 (by @luojiyin1987)
|
||||
- `baoyu-format-markdown`:使用 spawnSync 替代 shell 执行 autocorrect 命令 (by @luojiyin1987)
|
||||
|
||||
### 文档
|
||||
- 修正 CLAUDE 依赖说明 (by @luojiyin1987)
|
||||
- 将 markdown-to-html 添加到 README 工具技能列表 (by @luojiyin1987)
|
||||
|
||||
## 1.57.0 - 2026-03-08
|
||||
|
||||
### 新功能
|
||||
- 新增 ClawHub/OpenClaw 发布支持,包含同步脚本和 README 文档
|
||||
|
||||
### 重构
|
||||
- 为所有 skill 前言添加 openclaw 元数据,兼容 ClawHub 注册表
|
||||
- 全部 skill 中将 `SKILL_DIR` 统一重命名为 `baseDir`
|
||||
- `baoyu-danger-gemini-web`、`baoyu-danger-x-to-markdown`:使用动态脚本路径显示用法
|
||||
- `baoyu-comic`、`baoyu-xhs-images`:通过 skill 接口调用图片生成,不再直接调用脚本
|
||||
|
||||
## 1.56.1 - 2026-03-08
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-weibo`:简化头条文章图片插入逻辑,使用 Backspace 按键替代复杂的 deleteContents 方案,兼容 ProseMirror 编辑器
|
||||
|
||||
## 1.56.0 - 2026-03-08
|
||||
|
||||
### 新功能
|
||||
- `baoyu-article-illustrator`:预设优先选择流程,按内容类型分类的风格预设
|
||||
- `baoyu-xhs-images`:精简工作流从 6 步到 4 步,新增智能确认(快速/自定义/详细三种路径)
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-wechat`:通过文件选择器拦截改进图片上传可靠性
|
||||
|
||||
## 1.55.0 - 2026-03-08
|
||||
|
||||
### 新功能
|
||||
- `baoyu-article-illustrator`:新增 screen-print 风格和 `--preset` 快捷预设(如 tech-explainer、opinion-piece)
|
||||
- `baoyu-cover-image`:新增 screen-print 渲染风格和 duotone 调色板,包含 5 个新预设(poster-art、mondo 等)
|
||||
- `baoyu-xhs-images`:新增 screen-print 风格和 `--preset` 快捷预设,内置 23 个场景预设
|
||||
|
||||
### 文档
|
||||
- 为中英文 README 新增致谢章节,致敬相关开源项目
|
||||
|
||||
## 1.54.1 - 2026-03-07
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-x`:保持已填充的发帖窗口处于打开状态,方便用户手动检查并发布
|
||||
|
||||
### 文档
|
||||
- `baoyu-post-to-x`:补充默认帖子类型选择规则和手动发布流程说明
|
||||
- `README`:为中英文 README 新增 Star History 图表
|
||||
|
||||
## 1.54.0 - 2026-03-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-format-markdown`:优化标题和摘要生成,支持多风格候选(颠覆型、方案型、悬念型、数字型),新增禁用模式和钩子优先原则
|
||||
- `baoyu-markdown-to-html`:新增 `--cite` 选项,将普通外链转换为底部编号引用
|
||||
- `baoyu-post-to-wechat`:Markdown 输入默认启用底部引用,新增 `--no-cite` 标志可关闭
|
||||
- `baoyu-translate`:EXTEND.md 支持 `glossary_files` 加载外部术语表文件(Markdown 表格或 YAML 格式)
|
||||
- `baoyu-translate`:新增 frontmatter 转换规则,翻译时将源文章元数据字段添加 `source` 前缀
|
||||
|
||||
## 1.53.0 - 2026-03-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-url-to-markdown`:将渲染后的 HTML 快照保存为 `-captured.html`,与 Markdown 文件并列输出
|
||||
- `baoyu-url-to-markdown`:优先使用 Defuddle 转换,失败时自动回退到旧版 Readability/选择器提取器
|
||||
|
||||
## 1.52.0 - 2026-03-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-weibo`:新增 `--video` 视频上传支持(图片+视频最多 18 个文件)
|
||||
- `baoyu-post-to-weibo`:上传方式从剪贴板粘贴改为 `DOM.setFileInputFiles`,提升上传可靠性
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-weibo`:新增 Chrome 健康检查,无响应时自动重启
|
||||
- `baoyu-post-to-weibo`:发布前检查页面是否在微博首页,避免在错误页面操作
|
||||
|
||||
## 1.51.2 - 2026-03-06
|
||||
|
||||
### 修复
|
||||
- `release-skills`:将显式语言文件名模式(如 `CHANGELOG.de.md`)替换为通用模式,避免 Gen Agent Trust Hub URL 扫描器误报
|
||||
- `baoyu-infographic`:新增凭证/密钥剥离指令,解决 Snyk W007 不安全凭证处理审计问题
|
||||
|
||||
## 1.51.1 - 2026-03-06
|
||||
|
||||
### 重构
|
||||
- 统一 Chrome CDP profile 路径——所有 skill 共享 `baoyu-skills/chrome-profile`,不再各自独立目录
|
||||
- 修复 `baoyu-post-to-weibo` 错误复用 `x-browser-profile` 路径的问题
|
||||
|
||||
### 修复
|
||||
- 移除所有安装说明中的 `curl | bash` 远程代码执行模式
|
||||
- `md-to-html` 脚本强制仅允许 HTTPS 下载远程图片
|
||||
- 添加重定向次数限制(最多 5 次),防止无限重定向
|
||||
- 在 CLAUDE.md 中新增安全准则章节
|
||||
|
||||
## 1.51.0 - 2026-03-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-weibo`:新增微博发布技能——支持带图文本发布和头条文章,通过 Chrome CDP 自动化操作
|
||||
- `baoyu-format-markdown`:新增标题/摘要多候选项选择——生成 3 个候选供用户选择,支持 EXTEND.md 中的 `auto_select` 配置
|
||||
|
||||
## 1.50.0 - 2026-03-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-translate`:翻译风格预设从 4 种扩展到 9 种——新增学术、商务、幽默、口语化和优雅风格
|
||||
- `baoyu-translate`:新增 `--style` 命令行参数,支持按次指定翻译风格
|
||||
- `baoyu-translate`:将风格指令集成到子代理提示词模板
|
||||
|
||||
## 1.49.0 - 2026-03-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-format-markdown`:新增读者视角内容分析阶段——在应用格式之前先分析要点、结构和格式问题
|
||||
- `baoyu-format-markdown`:重构工作流从 8 步精简为 7 步,新增明确的格式化原则和完成报告模板
|
||||
- `baoyu-translate`:将步骤 2 的工作流机制提取到独立参考文件,精简 SKILL.md
|
||||
- `baoyu-translate`:扩展触发关键词(改成中文、快翻、本地化等),提升技能激活准确度
|
||||
- `baoyu-translate`:快速翻译模式下对长内容主动提示切换建议
|
||||
- `baoyu-translate`:分块时将 frontmatter 保存到 `chunks/frontmatter.md`
|
||||
|
||||
## 1.48.2 - 2026-03-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-translate`:在精翻工作流的审查和修订阶段新增比喻语言与情感忠实度检查
|
||||
- `baoyu-translate`:增强快速翻译模式,强制执行比喻语言的意义优先翻译原则
|
||||
|
||||
## 1.48.1 - 2026-03-05
|
||||
|
||||
### 新功能
|
||||
- `baoyu-translate`:在分析阶段新增比喻语言与隐喻映射——翻译前先解读隐喻、习语和隐含意义,避免字面直译
|
||||
- `baoyu-translate`:新增"意义优先于字面"、"比喻语言解读"、"情感忠实度"三项翻译原则,同步更新 SKILL.md、精翻工作流和子代理提示词模板
|
||||
|
||||
## 1.48.0 - 2026-03-05
|
||||
|
||||
### 新功能
|
||||
- `baoyu-translate`:为 chunk.ts 新增 `--output-dir` 选项——分块文件现在写入翻译输出目录而非源文件目录
|
||||
- `baoyu-translate`:优化精翻工作流——将审校拆分为批判性审查 + 修订(5→6 步),新增中日韩目标语言的欧化表达诊断
|
||||
|
||||
## 1.47.0 - 2026-03-05
|
||||
|
||||
### 新功能
|
||||
- 新增 `baoyu-translate` 翻译技能——支持快速/标准/精翻三种模式,自定义术语表、面向受众翻译、长文档自动分块并行翻译
|
||||
- 为所有技能的 EXTEND.md 偏好检测添加 PowerShell 跨平台支持
|
||||
|
||||
## 1.46.0 - 2026-03-05
|
||||
|
||||
### 新功能
|
||||
- 为 url-to-markdown 新增 `--output-dir` 选项,支持自定义输出目录并自动生成文件名
|
||||
|
||||
## 1.45.1 - 2026-03-05
|
||||
|
||||
### 重构
|
||||
- 将所有技能中硬编码的 `npx -y bun` 替换为 `${BUN_X}` 运行时变量——优先使用原生 `bun`,回退到 `npx -y bun`
|
||||
- 在 CLAUDE.md 中新增运行时检测章节,在所有 SKILL.md 的脚本目录说明中添加运行时解析步骤
|
||||
|
||||
## 1.45.0 - 2026-03-05
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-x`:X 文章发布后自动验证——检查残留占位符和图片数量是否正确
|
||||
- `baoyu-post-to-x`:增加 CDP 超时至 60 秒,图片插入间隔增加 3 秒 DOM 稳定等待,改善长文章发布稳定性
|
||||
|
||||
## 1.44.0 - 2026-03-05
|
||||
|
||||
### 新功能
|
||||
- `baoyu-url-to-markdown`:新增 `--download-media` 参数,支持下载图片和视频到本地目录,并将 Markdown 中的链接改写为本地路径
|
||||
- `baoyu-url-to-markdown`:从页面 meta 信息(og:image)提取封面图,写入 YAML front matter 的 `coverImage` 字段
|
||||
- `baoyu-url-to-markdown`:支持 `data-src` 懒加载图片提取(兼容微信公众号等站点)
|
||||
- `baoyu-url-to-markdown`:新增 EXTEND.md 偏好设置,支持首次使用引导配置媒体下载行为
|
||||
|
||||
## 1.43.2 - 2026-03-05
|
||||
|
||||
### 重构
|
||||
- `baoyu-url-to-markdown`:使用 defuddle 库替换自定义 HTML 提取逻辑(linkedom + Readability + Turndown),简化内容提取和 Markdown 转换
|
||||
|
||||
## 1.43.1 - 2026-03-02
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-x`:自动检测 WSL 环境,将 Chrome profile 路径解析为 Windows 本地路径,解决登录态丢失问题
|
||||
- `baoyu-post-to-wechat`:自动检测 WSL 环境,将 Chrome profile 路径解析为 Windows 本地路径,解决登录态丢失问题
|
||||
- `baoyu-danger-gemini-web`:WSL 自动检测 Chrome profile 路径;新增 `GEMINI_WEB_DEBUG_PORT` 环境变量支持固定调试端口
|
||||
- `baoyu-danger-x-to-markdown`:WSL 自动检测 Chrome profile 路径;新增 `X_DEBUG_PORT` 环境变量支持固定调试端口
|
||||
|
||||
## 1.43.0 - 2026-03-02
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-wechat`:支持通过环境变量覆盖浏览器调试端口(`WECHAT_BROWSER_DEBUG_PORT`)和配置目录(`WECHAT_BROWSER_PROFILE_DIR`)
|
||||
- `baoyu-post-to-x`:支持通过环境变量覆盖浏览器调试端口(`X_BROWSER_DEBUG_PORT`)和配置目录(`X_BROWSER_PROFILE_DIR`)
|
||||
|
||||
## 1.42.3 - 2026-03-02
|
||||
|
||||
### 修复
|
||||
- `baoyu-image-gen`:DashScope 宽高比映射改用标准预设尺寸匹配,避免自由计算产生无效分辨率
|
||||
|
||||
## 1.42.2 - 2026-03-01
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:内联渲染管线(移除子进程),修复 CJK 强调符号处理顺序,增强 modern 主题(GFM 警告块、排版改进)
|
||||
- `baoyu-post-to-wechat`:内置 Markdown 转换模块化渲染器,新增颜色支持,简化发布流程
|
||||
|
||||
## 1.42.1 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:将 render.ts 拆分为 cli、constants、extend-config、html-builder、renderer、themes、types 模块;本地打包代码高亮主题
|
||||
|
||||
## 1.42.0 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:合并 heritage 和 warm 为 modern 主题,新增主题默认颜色(default→蓝、grace→紫、simple→绿、modern→橙)
|
||||
- `baoyu-post-to-wechat`:EXTEND.md 新增默认颜色配置,首次设置增加 modern 主题和颜色选择
|
||||
|
||||
## 1.41.0 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:重命名主题(red→heritage、orange→warm),新增 13 个颜色预设、serif-cjk 字体、主题级样式默认值
|
||||
|
||||
## 1.40.1 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-image-gen`:明确模型解析优先级(EXTEND.md 优先于环境变量),生成图片时显示当前模型及切换方式
|
||||
|
||||
## 1.40.0 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-image-gen`:支持 OpenAI Chat Completions 端点生成图片 (by @zhao-newname)
|
||||
- `baoyu-markdown-to-html`:新增 CLI 自定义选项(--color、--font-family、--font-size、--code-theme、--mac-code-block、--line-number、--cite、--count、--legend)及 EXTEND.md 配置支持
|
||||
|
||||
## 1.39.0 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:新增红色主题(红金配色、宋体排版、传统书法风格)和橙色主题(暖色调现代风、圆角装饰、宽松行距)
|
||||
|
||||
## 1.38.0 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-danger-x-to-markdown`:支持文章内嵌推文渲染,以引用块形式显示作者信息和推文摘要
|
||||
- `baoyu-danger-x-to-markdown`:`--download-media` 复用已转换的 Markdown 文件,跳过重复抓取
|
||||
- `baoyu-danger-x-to-markdown`:推特图片下载升级至 4096x4096 高分辨率
|
||||
|
||||
### 修复
|
||||
- `baoyu-danger-x-to-markdown`:改进实体解析逻辑,通过逻辑键查找提升媒体和链接映射准确性
|
||||
- `baoyu-danger-x-to-markdown`:所有区块类型(标题、列表、引用块)支持尾随媒体展示
|
||||
|
||||
## 1.37.1 - 2026-02-27
|
||||
|
||||
### 修复
|
||||
- `baoyu-danger-gemini-web`:同步上游模型请求头并更新模型列表 (by @xkcoding)
|
||||
|
||||
## 1.37.0 - 2026-02-27
|
||||
|
||||
### 新功能
|
||||
- `baoyu-danger-x-to-markdown`:支持 X 文章内联链接渲染,将 LINK/MEDIA 实体映射为 Markdown 链接
|
||||
- `baoyu-danger-x-to-markdown`:输出目录使用基于内容的 slug,生成更有意义的文件夹名称
|
||||
- `baoyu-danger-x-to-markdown`:新增 atomic 媒体队列,支持无直接媒体引用的区块
|
||||
|
||||
## 1.36.0 - 2026-02-27
|
||||
|
||||
### 新功能
|
||||
- `baoyu-image-gen`:新增 `gemini-3.1-flash-image-preview` Google 多模态图片生成模型支持
|
||||
- `baoyu-image-gen`:优化首次使用引导流程,支持阻塞式偏好配置
|
||||
|
||||
### 修复
|
||||
- `baoyu-image-gen`:检测到 HTTP 代理时自动回退使用 curl 调用 Google API (by @liye71023326)
|
||||
|
||||
## 1.35.0 - 2026-02-24
|
||||
|
||||
### 新功能
|
||||
- `baoyu-image-gen`:新增 Replicate 图片生成服务,支持自定义模型配置 (by @justnode)
|
||||
- `baoyu-infographic`:新增 `dense-modules` 高密度模块布局及 3 种新风格(`morandi-journal`、`pop-laboratory`、`retro-pop-grid`),支持关键词快捷选择。高密度信息大图提示词来自 [AJ](https://waytoagi.feishu.cn/wiki/YG0zwalijihRREkgmPzcWRInnUg)
|
||||
|
||||
### 文档
|
||||
- `baoyu-image-gen`:补充 Replicate 模型配置说明文档
|
||||
|
||||
## 1.34.2 - 2026-02-25
|
||||
|
||||
### 文档
|
||||
- `baoyu-markdown-to-html`:明确主题解析优先级,先读取本技能与跨技能 EXTEND.md 的 `default_theme`,仅在未命中时询问用户。
|
||||
- `baoyu-post-to-wechat`:统一 markdown 转 HTML 的主题解析回退链(CLI `--theme` -> EXTEND.md `default_theme` -> `default`),并强制始终显式传入 `--theme` 参数。
|
||||
|
||||
## 1.34.1 - 2026-02-20
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-wechat`:修复上传进度检查在第二次迭代时崩溃的问题 (by @LyInfi)
|
||||
|
||||
## 1.34.0 - 2026-02-17
|
||||
|
||||
### 新功能
|
||||
- `baoyu-xhs-images`:新增参考图片链功能,确保多图系列的视觉一致性 (by @jeffrey94)
|
||||
|
||||
### 重构
|
||||
- `baoyu-article-illustrator`:将提示词文件创建设为生成图片前的阻断步骤,新增结构化提示词质量要求(ZONES / LABELS / COLORS / STYLE / ASPECT)和验证清单。
|
||||
|
||||
## 1.33.1 - 2026-02-14
|
||||
|
||||
### 重构
|
||||
- `baoyu-post-to-x`:将手写 markdown 解析器替换为 marked 生态系统,用于 X Articles HTML 转换。
|
||||
|
||||
### 文档
|
||||
- `baoyu-post-to-x`:移除所有脚本的 `--submit` 参数;明确脚本仅将内容填充到浏览器,由用户手动审核和发布。
|
||||
|
||||
## 1.33.0 - 2026-02-13
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-x`:新增环境预检脚本(`check-paste-permissions.ts`);新增 Chrome 调试端口冲突的故障排查说明;将固定等待替换为图片上传轮询验证(最长 15 秒)。
|
||||
- `baoyu-post-to-wechat`:新增环境预检脚本(`check-permissions.ts`),检查 Chrome、配置文件隔离、Bun、辅助功能、剪贴板、粘贴按键和 API 凭据。
|
||||
|
||||
## 1.32.0 - 2026-02-12
|
||||
|
||||
### 新功能
|
||||
- `baoyu-danger-x-to-markdown`:新增 `--download-media` 参数,支持将图片/视频下载到本地并将 markdown 链接改写为相对路径;新增媒体本地化模块;新增首次使用 EXTEND.md 偏好设置;在 frontmatter 中输出 `coverImage`。
|
||||
|
||||
### 重构
|
||||
- `baoyu-danger-x-to-markdown`:frontmatter 字段改为 camelCase(`tweetCount`、`coverImage`、`requestedUrl` 等)。
|
||||
- `baoyu-format-markdown`:将主 frontmatter 字段从 `featureImage` 更名为 `coverImage`(兼容 `featureImage`)。
|
||||
- `baoyu-post-to-wechat`:封面图片 frontmatter 查找顺序中优先使用 `coverImage`。
|
||||
|
||||
## 1.31.2 - 2026-02-10
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-wechat`:修复 Windows 上 PowerShell 剪贴板复制失败的问题(`param()`/`-Path` 与 `-Command` 参数不兼容)。
|
||||
- `baoyu-post-to-x`:修复 Windows 上 PowerShell 剪贴板复制(同上);修复 `getScriptDir()` 在 Windows 上返回无效路径(`/C:/...` 前缀)。
|
||||
|
||||
## 1.31.1 - 2026-02-10
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-wechat`:适配微信新版 UI — 图文更名为贴图;新增 ProseMirror 编辑器支持(兼容旧版编辑器);新增备用文件上传选择器;新增上传进度监控;改进保存按钮检测并增加 toast 验证。
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-wechat`:摘要超过 120 字符时在标点处截断;修复封面图片相对路径解析。
|
||||
- `baoyu-post-to-x`:修复 macOS 上 Chrome 启动问题(使用 `open -na`);修复封面图片相对路径解析。
|
||||
|
||||
## 1.31.0 - 2026-02-07
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-wechat`:新增评论控制设置(`need_open_comment`、`only_fans_can_comment`);新增封面图片回退链(CLI → frontmatter → `imgs/cover.png` → 首张内联图片);新增作者优先级解析;新增首次使用引导流程和 EXTEND.md 偏好配置。
|
||||
|
||||
## 1.30.3 - 2026-02-06
|
||||
|
||||
### 重构
|
||||
- `baoyu-article-illustrator`:优化 SKILL.md 从 197 行精简至 150 行(减少 24%);采用渐进式披露模式,主文件提供简洁概览,详细内容通过引用文件提供。
|
||||
|
||||
## 1.30.2 - 2026-02-06
|
||||
|
||||
### 重构
|
||||
- `baoyu-cover-image`:优化 SKILL.md 从 532 行精简至 233 行(减少 56%);将参考图片处理流程提取到 `references/workflow/reference-images.md`;画廊改为纯值表格并链接到详细参考文件。
|
||||
|
||||
## 1.30.1 - 2026-02-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-image-gen`:新增 OpenAI GPT Image edits 支持参考图片(`--ref`);提供 ref 时自动选择 Google 或 OpenAI。
|
||||
|
||||
### 修复
|
||||
- `baoyu-image-gen`:将 ref 相关警告改为明确错误提示;新增参考图片验证。
|
||||
- `baoyu-cover-image`:增强参考图片分析,使用深度提取模板;要求 MUST INCORPORATE 章节以包含具体可复现的视觉元素。
|
||||
|
||||
## 1.30.0 - 2026-02-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-cover-image`:新增字体维度,支持 4 种字体风格(clean、handwritten、serif、display);包含自动选择规则、兼容性矩阵和 `warm-flat` 风格预设。
|
||||
|
||||
## 1.29.0 - 2026-02-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-image-gen`:新增 EXTEND.md 配置支持,补充配置 schema 文档并在脚本运行时读取偏好设置 (by @kingdomad)。
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-wechat`:修复公众号文章发布时标题和有序列表编号重复问题 (by @NantesCheval)。
|
||||
- `baoyu-url-to-markdown`:将正则转换升级为多策略正文抽取 + Turndown 转换,提升 Substack 类页面的噪声过滤能力。
|
||||
|
||||
## 1.28.4 - 2026-02-03
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:从 YAML frontmatter 生成 author 和 description meta 标签;自动去除 frontmatter 值两端的引号(支持中英文引号)。
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-wechat`:移除图片粘贴后产生的多余空行;修复摘要填充时机,改为内容粘贴后填写(避免被覆盖)。
|
||||
|
||||
## 1.28.3 - 2026-02-03
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-wechat`:修复占位符匹配问题(`WECHATIMGPH_1` 错误匹配 `WECHATIMGPH_10`)。
|
||||
|
||||
## 1.28.2 - 2026-02-03
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-x`:复用已有 Chrome 实例;修复占位符匹配问题(`XIMGPH_1` 错误匹配 `XIMGPH_10`);改进图片按占位符序号排序;使用 `execCommand` 提高占位符删除可靠性。
|
||||
|
||||
## 1.28.1 - 2026-02-02
|
||||
|
||||
### 重构
|
||||
- `baoyu-article-illustrator`:简化主 SKILL.md,将详细步骤提取到 `workflow.md`;新增 Core Styles 快速选择层(vector、minimal-flat、sci-fi、hand-drawn、editorial、scene);新增 `vector-illustration` 作为推荐默认风格;新增插图目的(information/visualization/imagination)以优化类型/风格推荐;在提示词构建中新增默认构图要求、人物渲染指南和文本样式规则。
|
||||
|
||||
## 1.28.0 - 2026-02-01
|
||||
|
||||
### 新功能
|
||||
- `baoyu-cover-image`:新增参考图片支持(`--ref` 参数),支持 direct/style/palette 三种用法;新增视觉元素库,按主题分类图标词汇。
|
||||
- `baoyu-article-illustrator`:新增参考图片支持,支持 direct/style/palette 三种用法。
|
||||
- `baoyu-post-to-wechat`:新增 `newspic` 图文消息类型支持。
|
||||
|
||||
### 重构
|
||||
- `baoyu-cover-image`、`baoyu-article-illustrator`、`baoyu-comic`、`baoyu-xhs-images`:强化首次设置为阻塞操作,必须在其他工作流步骤之前完成。
|
||||
- `baoyu-cover-image`:移除标题字符数限制,使用原始来源标题。
|
||||
|
||||
## 1.26.1 - 2026-01-29
|
||||
|
||||
### 新功能
|
||||
- `baoyu-article-illustrator`、`baoyu-comic`、`baoyu-cover-image`、`baoyu-infographic`、`baoyu-slide-deck`、`baoyu-xhs-images`:新增文件备份规则,覆盖前自动将现有源文件、提示词和图片重命名为带时间戳后缀的备份文件。
|
||||
|
||||
### 修复
|
||||
- `baoyu-xhs-images`:移除 `notebook` 风格(保留 10 种风格)。
|
||||
|
||||
## 1.26.0 - 2026-01-29
|
||||
|
||||
### 新功能
|
||||
- `baoyu-xhs-images`:新增 `notebook` 风格(水彩渲染手绘信息图 + 莫兰迪配色)和 `study-notes` 风格(真实手写照片美学)。
|
||||
- `baoyu-xhs-images`:新增 `mindmap`(中心发散式)和 `quadrant`(四象限)布局。
|
||||
|
||||
## 1.25.4 - 2026-01-29
|
||||
|
||||
### 修复
|
||||
- `baoyu-markdown-to-html`:生成带 `data-local-path` 属性的 `<img>` 标签,而非纯文本占位符。
|
||||
- `baoyu-post-to-wechat`:修复 API 发布时从 `data-local-path` 属性读取图片路径;修复发布 HTML 文件时从对应 `.md` 的 frontmatter 提取标题和封面图。
|
||||
- `baoyu-post-to-wechat`:修复命令行参数解析,正确跳过未知参数;新增 `--summary` 参数支持。
|
||||
- `baoyu-post-to-wechat`:修复浏览器发布模式,粘贴前将 `<img>` 标签转换回文本占位符。
|
||||
|
||||
## 1.25.3 - 2026-01-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-format-markdown`:新增内容类型检测,对已有 markdown 格式的文件提供用户确认选项;新增 CJK 配对标点处理,将括号、引号等标点移出加粗标记外。
|
||||
|
||||
## 1.25.2 - 2026-01-28
|
||||
|
||||
### 文档
|
||||
- `baoyu-post-to-wechat`:README 新增微信公众号 API 凭证配置说明。
|
||||
|
||||
## 1.25.1 - 2026-01-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:新增中文内容预检查,建议在转换前使用 `baoyu-format-markdown` 格式化以修复加粗标点问题。
|
||||
|
||||
## 1.25.0 - 2026-01-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-format-markdown`:新增 markdown 格式化技能,支持 frontmatter、排版优化和中英文空格处理。
|
||||
- `baoyu-markdown-to-html`:新增 markdown 转 HTML 技能,支持微信兼容主题、代码高亮、数学公式、PlantUML 和 alerts。
|
||||
- `baoyu-post-to-wechat`:新增 API 发布方式和外部主题支持。
|
||||
|
||||
## 1.24.4 - 2026-01-28
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-x`:修复封面图上传后 Apply 按钮点击问题;增加重试逻辑并等待弹窗关闭后再继续。
|
||||
|
||||
## 1.24.3 - 2026-01-28
|
||||
|
||||
### 文档
|
||||
- 在修改工作流中强调先更新提示词文件再生成图片(article-illustrator、slide-deck、xhs-images、cover-image、comic)。
|
||||
|
||||
## 1.24.2 - 2026-01-28
|
||||
|
||||
### 重构
|
||||
- `baoyu-image-gen`:默认改为顺序生成图片;并行生成需明确请求。
|
||||
|
||||
## 1.24.1 - 2026-01-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-image-gen`:新增阿里云通义万象(DashScope)文生图模型支持 (by @JianJang2017)。
|
||||
|
||||
### 文档
|
||||
- README 中新增阿里云文生图模型配置说明。
|
||||
|
||||
## 1.24.0 - 2026-01-27
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-wechat`:复用已打开的 Chrome 浏览器,无需关闭所有窗口 (by @AliceLJY)。
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-wechat`:改进标题提取,支持 h1/h2 标题;新增摘要自动填充和粘贴/输入后内容验证;支持 HTML meta 标签属性顺序灵活匹配。
|
||||
|
||||
### 文档
|
||||
- `release-skills`:在发布流程中新增第三方贡献者署名规则。
|
||||
- 补全历史 changelog 中缺失的第三方贡献者署名。
|
||||
|
||||
## 1.23.1 - 2026-01-27
|
||||
|
||||
### 修复
|
||||
- `baoyu-compress-image`:压缩后将原始文件重命名为 `_original` 备份,不再删除。
|
||||
|
||||
## 1.23.0 - 2026-01-26
|
||||
|
||||
### 重构
|
||||
- `baoyu-cover-image`:将 20 种固定风格替换为五维系统(类型 × 配色 × 渲染 × 文字 × 氛围)。9 种配色方案 × 6 种渲染风格 = 54 种组合。新增风格预设实现向后兼容,v2→v3 配置迁移,以及新的引用文件结构(`palettes/`、`renderings/`、`workflow/`)。
|
||||
|
||||
## 1.22.0 - 2026-01-25
|
||||
|
||||
### 新功能
|
||||
- `baoyu-article-illustrator`:新增 `imgs-subdir` 输出目录选项;改进风格选择,始终询问并展示 EXTEND.md 中的 preferred_style。
|
||||
- `baoyu-cover-image`:新增 `default_output_dir` 偏好设置,支持 `same-dir`、`imgs-subdir` 和 `independent` 选项,新增 Step 1.5 输出目录选择流程。
|
||||
- `baoyu-post-to-wechat`:发布前新增主题选择(default/grace/simple);新增 HTML 预览步骤;图片占位符简化为 `WECHATIMGPH_N` 格式;重构复制粘贴为跨平台辅助函数。
|
||||
|
||||
### 重构
|
||||
- `baoyu-post-to-x`:图片占位符从 `[[IMAGE_PLACEHOLDER_N]]` 简化为 `XIMGPH_N` 格式。
|
||||
|
||||
## 1.21.4 - 2026-01-25
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-wechat`:新增 Windows 兼容性——使用 `fileURLToPath` 正确解析路径,将系统依赖的复制粘贴工具(osascript/xdotool)替换为 CDP 键盘事件,实现跨平台支持 (by @JadeLiang003)。
|
||||
- `baoyu-post-to-wechat`:修复 Windows 兼容性 PR 引入的回退问题——修正错误的 `-fixed` 文件名引用、恢复 frontmatter 引号剥离、恢复 `--title` CLI 参数、修复摘要提取逻辑以正确跳过标题/引用/列表、修复单横线参数解析、移除调试日志。
|
||||
- `baoyu-article-illustrator`、`baoyu-cover-image`、`baoyu-xhs-images`:移除水印配置中的透明度选项。
|
||||
|
||||
## 1.21.3 - 2026-01-24
|
||||
|
||||
### 重构
|
||||
- `baoyu-article-illustrator`:简化 SKILL.md,提取内容至引用文件——新增 `references/usage.md` 用于命令语法,`references/prompt-construction.md` 用于提示词模板。工作流从 5 步重组为 6 步,新增 Pre-check 预检阶段。新增 `default_output_dir` 偏好设置选项。
|
||||
|
||||
## 1.21.2 - 2026-01-24
|
||||
|
||||
### 新功能
|
||||
- `baoyu-image-gen`:添加并行生成文档,推荐使用 4 个并发 subagent 进行批量操作。
|
||||
|
||||
### 文档
|
||||
- `release-skills`:新增按 skill/module 分组提交流程和发布前用户确认步骤。
|
||||
|
||||
## 1.21.1 - 2026-01-24
|
||||
|
||||
### 文档
|
||||
- `baoyu-comic`:在角色参考图生成后添加压缩步骤,减少作为参考图使用时的 token 消耗。
|
||||
|
||||
## 1.21.0 - 2026-01-24
|
||||
|
||||
### 新功能
|
||||
- `baoyu-cover-image`:扩展宽高比选项——新增 4:3、3:2、3:4 比例;默认值从 2.35:1 改为 16:9 以提高通用性。现在除非通过 `--aspect` 标志明确指定,否则始终确认宽高比。
|
||||
- `baoyu-image-gen`:重构 Google provider 以统一支持 Gemini 多模态和 Imagen 模型。为 Gemini 模型新增 `--imageSize` 参数支持(1K/2K/4K)。
|
||||
|
||||
## 1.20.0 - 2026-01-24
|
||||
|
||||
### 新功能
|
||||
- `baoyu-cover-image`:从类型 × 风格二维系统升级为**四维系统**——新增 `--text` 维度(none 无文字、title-only 仅标题、title-subtitle 标题+副标题、text-rich 丰富文字)控制文字密度,新增 `--mood` 维度(subtle 低调、balanced 平衡、bold 醒目)控制情感强度。新增 `--quick` 标志跳过确认,直接使用自动选择。
|
||||
|
||||
### 文档
|
||||
- `baoyu-cover-image`:新增维度参考文件——`references/dimensions/text.md`(文字密度级别)和 `references/dimensions/mood.md`(氛围强度级别)。
|
||||
- `baoyu-cover-image`:更新 base-prompt、first-time-setup 和 preferences-schema 以支持新的四维系统及 v2 配置模式。
|
||||
- `README.md`、`README.zh.md`:更新 baoyu-cover-image 文档,反映新的四维系统及 `--text`、`--mood`、`--quick` 选项。
|
||||
|
||||
## 1.19.0 - 2026-01-24
|
||||
|
||||
### 新功能
|
||||
@@ -114,7 +685,7 @@
|
||||
## 1.14.0 - 2026-01-22
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-x`:改进视频就绪检测,提升视频发布稳定性。
|
||||
- `baoyu-post-to-x`:改进视频就绪检测,提升视频发布稳定性 (by @fkysly)。
|
||||
|
||||
### 文档
|
||||
- `baoyu-slide-deck`:SKILL.md 全面增强——新增幻灯片数量指南(推荐 8-25 张,最多 30 张)、受众指南表格及各受众特定原则、风格选择原则与内容类型推荐、布局选择技巧与常见错误提示、视觉层次原则、内容密度指南(麦肯锡风格高密度原则)、配色选择指南、字体排版原则与字体推荐(中英文字体及多语言搭配方案)、视觉元素参考(背景处理、字体处理、几何装饰)。
|
||||
@@ -129,6 +700,9 @@
|
||||
|
||||
## 1.12.0 - 2026-01-21
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-x`:新增引用推文(Quote Tweet)支持 (by @threehotpot-bot)。
|
||||
|
||||
### 重构
|
||||
- `baoyu-post-to-x`:提取公共工具函数到 `x-utils.ts`——将 `x-article.ts`、`x-browser.ts`、`x-quote.ts`、`x-video.ts` 中重复的 Chrome 检测、CDP 连接、剪贴板操作等功能整合为统一的可复用模块。
|
||||
|
||||
@@ -144,7 +718,7 @@
|
||||
## 1.10.0 - 2026-01-21
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-x`:新增视频发布支持——新增 `x-video.ts` 脚本,支持发布带视频的推文(MP4、MOV、WebM 格式)。支持预览模式,自动处理视频上传等待。
|
||||
- `baoyu-post-to-x`:新增视频发布支持——新增 `x-video.ts` 脚本,支持发布带视频的推文(MP4、MOV、WebM 格式)。支持预览模式,自动处理视频上传等待 (by @fkysly)。
|
||||
|
||||
## 1.9.0 - 2026-01-20
|
||||
|
||||
@@ -200,7 +774,7 @@
|
||||
## 1.4.1 - 2026-01-18
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-x`:支持 X Articles 多语言 UI 选择器(感谢 [@ianchenx](https://github.com/ianchenx) 贡献)。
|
||||
- `baoyu-post-to-x`:支持 X Articles 多语言 UI 选择器 (by @ianchenx)。
|
||||
|
||||
## 1.4.0 - 2026-01-18
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project Overview
|
||||
|
||||
Claude Code marketplace plugin providing AI-powered content generation skills. Skills use Gemini Web API (reverse-engineered) for text/image generation and Chrome CDP for browser automation.
|
||||
Claude Code marketplace plugin providing AI-powered content generation skills. Current version: **1.59.1**. Skills use official AI APIs (OpenAI, Google, DashScope, Replicate) or the reverse-engineered Gemini Web API for text/image generation, and Chrome CDP for browser automation.
|
||||
|
||||
## Architecture
|
||||
|
||||
Skills are organized into three plugin categories in `marketplace.json`:
|
||||
Skills are organized into three plugin categories in `.claude-plugin/marketplace.json`:
|
||||
|
||||
```
|
||||
skills/
|
||||
@@ -19,16 +19,27 @@ skills/
|
||||
│ ├── baoyu-article-illustrator/ # Smart illustration placement
|
||||
│ ├── baoyu-comic/ # Knowledge comics (Logicomix/Ohmsha style)
|
||||
│ ├── baoyu-post-to-x/ # X/Twitter posting automation
|
||||
│ └── baoyu-post-to-wechat/ # WeChat Official Account posting
|
||||
│ ├── baoyu-post-to-wechat/ # WeChat Official Account posting
|
||||
│ ├── baoyu-post-to-weibo/ # Weibo posting (text, images, videos, articles)
|
||||
│ └── baoyu-infographic/ # Professional infographics (21 layouts × 20 styles)
|
||||
│
|
||||
├── [ai-generation-skills] # AI-powered generation backends
|
||||
│ └── baoyu-danger-gemini-web/ # Gemini API wrapper (text + image gen)
|
||||
│ ├── baoyu-danger-gemini-web/ # Gemini Web API wrapper (text + image gen)
|
||||
│ └── baoyu-image-gen/ # Official API image gen (OpenAI, Google, DashScope, Replicate)
|
||||
│
|
||||
└── [utility-skills] # Utility tools for content processing
|
||||
├── baoyu-danger-x-to-markdown/ # X/Twitter content to markdown
|
||||
└── baoyu-compress-image/ # Image compression
|
||||
├── baoyu-compress-image/ # Image compression (WebP/PNG via sips/cwebp/Sharp)
|
||||
├── baoyu-url-to-markdown/ # Fetch any URL → markdown via Chrome CDP
|
||||
├── baoyu-format-markdown/ # Format/beautify plain text or markdown files
|
||||
├── baoyu-markdown-to-html/ # Markdown → styled HTML (WeChat-compatible themes)
|
||||
└── baoyu-translate/ # Multi-mode translation (quick/normal/refined)
|
||||
```
|
||||
|
||||
Top-level `scripts/` directory contains repository maintenance utilities:
|
||||
- `scripts/sync-clawhub.sh` - Publish skills to ClawHub/OpenClaw registry
|
||||
- `scripts/sync-md-to-wechat.sh` - Sync markdown content to WeChat
|
||||
|
||||
**Plugin Categories**:
|
||||
| Category | Description |
|
||||
|----------|-------------|
|
||||
@@ -37,35 +48,129 @@ skills/
|
||||
| `utility-skills` | Helper tools for content processing (conversion, compression) |
|
||||
|
||||
Each skill contains:
|
||||
- `SKILL.md` - YAML front matter (name, description) + documentation
|
||||
- `scripts/` - TypeScript implementations
|
||||
- `prompts/system.md` - AI generation guidelines (optional)
|
||||
- `SKILL.md` - YAML front matter (name, description, version, openclaw metadata) + documentation
|
||||
- `scripts/` - TypeScript implementations (optional, pure-prompt skills omit this)
|
||||
- `references/` - Extended reference files linked from SKILL.md (optional)
|
||||
- `prompts/` - AI prompt templates (optional, used by some content skills)
|
||||
|
||||
## Running Skills
|
||||
|
||||
All scripts run via Bun (no build step):
|
||||
All scripts are TypeScript, executed via Bun runtime (no build step).
|
||||
|
||||
### Runtime Detection (`${BUN_X}`)
|
||||
|
||||
Before running any script, the agent MUST detect the runtime **once per session** and set `${BUN_X}`:
|
||||
|
||||
```bash
|
||||
npx -y bun skills/<skill>/scripts/main.ts [options]
|
||||
# Detect runtime (run once, reuse result)
|
||||
if command -v bun &>/dev/null; then
|
||||
BUN_X="bun"
|
||||
elif command -v npx &>/dev/null; then
|
||||
BUN_X="npx -y bun"
|
||||
else
|
||||
echo "Error: Neither bun nor npx found. Install bun: brew install oven-sh/bun/bun (macOS) or npm install -g bun"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
| Priority | Condition | `${BUN_X}` value | Notes |
|
||||
|----------|-----------|-------------------|-------|
|
||||
| 1 | `bun` installed | `bun` | Fastest, native execution |
|
||||
| 2 | `npx` available | `npx -y bun` | Downloads bun on first run via npm |
|
||||
| 3 | Neither found | Error + install guide | `brew install oven-sh/bun/bun` (macOS) or `npm install -g bun` |
|
||||
|
||||
### Script Execution
|
||||
|
||||
```bash
|
||||
${BUN_X} skills/<skill>/scripts/main.ts [options]
|
||||
```
|
||||
|
||||
Examples:
|
||||
```bash
|
||||
# Text generation
|
||||
npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts "Hello"
|
||||
${BUN_X} skills/baoyu-danger-gemini-web/scripts/main.ts "Hello"
|
||||
|
||||
# Image generation
|
||||
npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts --prompt "A cat" --image cat.png
|
||||
${BUN_X} skills/baoyu-danger-gemini-web/scripts/main.ts --prompt "A cat" --image cat.png
|
||||
|
||||
# From prompt files
|
||||
npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts --promptfiles system.md content.md --image out.png
|
||||
${BUN_X} skills/baoyu-danger-gemini-web/scripts/main.ts --promptfiles system.md content.md --image out.png
|
||||
```
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
- **Bun**: TypeScript runtime (via `npx -y bun`)
|
||||
- **Chrome**: Required for `baoyu-danger-gemini-web` auth and `baoyu-post-to-x` automation
|
||||
- **No npm packages**: Self-contained TypeScript, no external dependencies
|
||||
- **Bun**: TypeScript runtime (native `bun` preferred, fallback `npx -y bun`)
|
||||
- **Chrome**: Required for `baoyu-danger-gemini-web` auth, `baoyu-post-to-x`, `baoyu-post-to-wechat`, `baoyu-post-to-weibo`, and `baoyu-url-to-markdown`
|
||||
- **npm packages (per skill)**: Some skill subprojects include `package.json`/lockfiles and require dependency installation in their own `scripts/` directories
|
||||
- **Image generation APIs**: `baoyu-image-gen` requires at least one API key (OpenAI, Google, DashScope, or Replicate)
|
||||
|
||||
## Chrome Profile (Unified)
|
||||
|
||||
All skills that use Chrome CDP share a **single** profile directory. Do NOT create per-skill profiles.
|
||||
|
||||
| Platform | Default Path |
|
||||
|----------|-------------|
|
||||
| macOS | `~/Library/Application Support/baoyu-skills/chrome-profile` |
|
||||
| Linux | `$XDG_DATA_HOME/baoyu-skills/chrome-profile` (fallback `~/.local/share/baoyu-skills/chrome-profile`) |
|
||||
| Windows | `%APPDATA%/baoyu-skills/chrome-profile` |
|
||||
| WSL | Windows home `/.local/share/baoyu-skills/chrome-profile` |
|
||||
|
||||
**Environment variable override**: `BAOYU_CHROME_PROFILE_DIR` (takes priority, all skills respect it).
|
||||
|
||||
Each skill also accepts its own legacy env var as fallback (e.g., `X_BROWSER_PROFILE_DIR`), but new skills should only use `BAOYU_CHROME_PROFILE_DIR`.
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
When adding a new skill that needs Chrome CDP:
|
||||
|
||||
```typescript
|
||||
function getDefaultProfileDir(): string {
|
||||
const override = process.env.BAOYU_CHROME_PROFILE_DIR?.trim();
|
||||
if (override) return path.resolve(override);
|
||||
const base = process.platform === 'darwin'
|
||||
? path.join(os.homedir(), 'Library', 'Application Support')
|
||||
: process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share');
|
||||
return path.join(base, 'baoyu-skills', 'chrome-profile');
|
||||
}
|
||||
```
|
||||
|
||||
## Security Guidelines
|
||||
|
||||
### No Piped Shell Installs
|
||||
|
||||
**NEVER** use `curl | bash` or `wget | sh` patterns in code, docs, or error messages. Use package managers instead:
|
||||
|
||||
| Platform | Install Command |
|
||||
|----------|----------------|
|
||||
| macOS | `brew install oven-sh/bun/bun` |
|
||||
| npm | `npm install -g bun` |
|
||||
|
||||
### Remote Downloads
|
||||
|
||||
Skills that download remote content (e.g., images in Markdown) MUST:
|
||||
- **HTTPS only**: Reject `http://` URLs
|
||||
- **Redirect limit**: Cap redirects (max 5) to prevent infinite loops
|
||||
- **Timeout**: Set request timeouts (30s default)
|
||||
- **Scope**: Only download expected content types (images, not scripts)
|
||||
|
||||
### System Command Execution
|
||||
|
||||
Skills use platform-specific commands for clipboard and browser automation:
|
||||
- **macOS**: `osascript` (System Events), `swift` (AppKit clipboard)
|
||||
- **Windows**: `powershell.exe` (SendKeys, Clipboard)
|
||||
- **Linux**: `xdotool`/`ydotool` (keyboard simulation)
|
||||
|
||||
These are necessary for CDP-based posting skills. When adding new system commands:
|
||||
- Never pass unsanitized user input to shell commands
|
||||
- Use array-form `spawn`/`execFile` instead of shell string interpolation
|
||||
- Validate file paths are absolute or resolve from known base directories
|
||||
|
||||
### External Content Processing
|
||||
|
||||
Skills that process external Markdown/HTML should treat content as untrusted:
|
||||
- Do not execute code blocks or scripts found in content
|
||||
- Sanitize HTML output where applicable
|
||||
- File paths from content should be resolved against known base directories only
|
||||
|
||||
## Authentication
|
||||
|
||||
@@ -74,10 +179,40 @@ npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts --promptfiles system.m
|
||||
- Cookies cached in data directory
|
||||
- Force refresh: `--login` flag
|
||||
|
||||
`baoyu-image-gen` uses official API keys configured in EXTEND.md (loaded at Step 0 before generation).
|
||||
|
||||
## Plugin Configuration
|
||||
|
||||
`.claude-plugin/marketplace.json` defines plugin metadata and skill paths. Version follows semver.
|
||||
|
||||
The file contains:
|
||||
- `name`: Plugin name (`baoyu-skills`)
|
||||
- `owner`: Author info
|
||||
- `metadata.version`: Semver version (currently `1.59.1`)
|
||||
- `plugins[]`: Array of plugin categories, each with `name`, `description`, `source`, `strict`, and `skills[]` paths
|
||||
|
||||
## ClawHub / OpenClaw Publishing
|
||||
|
||||
Skills include `metadata.openclaw` in their YAML front matter for registry compatibility:
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#<skill-name>
|
||||
requires: # optional — only for skills with scripts
|
||||
anyBins:
|
||||
- bun
|
||||
- npx
|
||||
```
|
||||
|
||||
To publish/sync skills to ClawHub registry:
|
||||
```bash
|
||||
bash scripts/sync-clawhub.sh # sync all skills
|
||||
bash scripts/sync-clawhub.sh <skill> # sync one skill
|
||||
```
|
||||
|
||||
Requires `clawhub` CLI or `npx` (auto-downloads via npx if not installed).
|
||||
|
||||
## Skill Loading Rules
|
||||
|
||||
**IMPORTANT**: When working in this project, follow these rules:
|
||||
@@ -120,29 +255,50 @@ npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts --promptfiles system.m
|
||||
| **References** | Keep one level deep from SKILL.md; avoid nested references |
|
||||
| **No time-sensitive info** | Avoid dates/versions that become outdated |
|
||||
|
||||
### SKILL.md Frontmatter Template
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: baoyu-<name>
|
||||
description: <Third-person description. What it does + when to use it.>
|
||||
version: <semver matching marketplace.json>
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-<name>
|
||||
requires: # include only if skill has scripts
|
||||
anyBins:
|
||||
- bun
|
||||
- npx
|
||||
---
|
||||
```
|
||||
|
||||
### Steps
|
||||
|
||||
1. Create `skills/baoyu-<name>/SKILL.md` with YAML front matter
|
||||
1. Create `skills/baoyu-<name>/SKILL.md` with YAML front matter (see template above)
|
||||
- Directory name: `baoyu-<name>`
|
||||
- SKILL.md `name` field: `baoyu-<name>`
|
||||
2. Add TypeScript in `skills/baoyu-<name>/scripts/`
|
||||
- Always include `metadata.openclaw.homepage`
|
||||
- Include `metadata.openclaw.requires.anyBins` only when the skill has executable scripts
|
||||
2. Add TypeScript in `skills/baoyu-<name>/scripts/` (if applicable)
|
||||
3. Add prompt templates in `skills/baoyu-<name>/prompts/` if needed
|
||||
4. **Choose the appropriate category** and register in `marketplace.json`:
|
||||
- `content-skills`: For content generation/publishing (images, slides, posts)
|
||||
- `ai-generation-skills`: For AI backend capabilities
|
||||
- `utility-skills`: For helper tools (conversion, compression)
|
||||
- If none fit, create a new category with descriptive name
|
||||
5. **Add Script Directory section** to SKILL.md (see template below)
|
||||
5. **Add Script Directory section** to SKILL.md (see template below) if skill has scripts
|
||||
6. **Add openclaw metadata** to SKILL.md frontmatter (required for ClawHub registry)
|
||||
|
||||
### Choosing a Category
|
||||
|
||||
| If your skill... | Use category |
|
||||
|------------------|--------------|
|
||||
| Generates visual content (images, slides, comics) | `content-skills` |
|
||||
| Publishes to platforms (X, WeChat, etc.) | `content-skills` |
|
||||
| Provides AI generation backend | `ai-generation-skills` |
|
||||
| Converts or processes content | `utility-skills` |
|
||||
| Generates visual content (images, slides, comics, infographics) | `content-skills` |
|
||||
| Publishes to platforms (X, WeChat, Weibo, etc.) | `content-skills` |
|
||||
| Provides AI generation backend (official API or reverse-engineered) | `ai-generation-skills` |
|
||||
| Converts or processes content (URL→md, md→html, format) | `utility-skills` |
|
||||
| Compresses or optimizes files | `utility-skills` |
|
||||
| Translates documents | `utility-skills` |
|
||||
|
||||
**Creating a new category**: If the skill doesn't fit existing categories, add a new plugin object to `marketplace.json` with:
|
||||
- `name`: Descriptive kebab-case name (e.g., `analytics-skills`)
|
||||
@@ -174,9 +330,10 @@ Every SKILL.md with scripts MUST include this section after Usage:
|
||||
**Important**: All scripts are located in the `scripts/` subdirectory of this skill.
|
||||
|
||||
**Agent Execution Instructions**:
|
||||
1. Determine this SKILL.md file's directory path as `SKILL_DIR`
|
||||
2. Script path = `${SKILL_DIR}/scripts/<script-name>.ts`
|
||||
3. Replace all `${SKILL_DIR}` in this document with the actual path
|
||||
1. Determine this SKILL.md file's directory path as `{baseDir}`
|
||||
2. Script path = `{baseDir}/scripts/<script-name>.ts`
|
||||
3. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun
|
||||
4. Replace all `{baseDir}` and `${BUN_X}` in this document with actual values
|
||||
|
||||
**Script Reference**:
|
||||
| Script | Purpose |
|
||||
@@ -185,7 +342,9 @@ Every SKILL.md with scripts MUST include this section after Usage:
|
||||
| `scripts/other.ts` | Other functionality |
|
||||
```
|
||||
|
||||
When referencing scripts in workflow sections, use `${SKILL_DIR}/scripts/<name>.ts` so agents can resolve the correct path.
|
||||
When referencing scripts in workflow sections, use `${BUN_X} {baseDir}/scripts/<name>.ts` so agents can resolve the correct runtime and path.
|
||||
|
||||
> **Note**: The path variable was renamed from `SKILL_DIR` / `${SKILL_DIR}` to `baseDir` / `{baseDir}` (no `${}` wrapper) in v1.57.0. All existing SKILL.md files use `{baseDir}`.
|
||||
|
||||
### Progressive Disclosure
|
||||
|
||||
@@ -234,7 +393,7 @@ Use this template when implementing image generation in skills:
|
||||
### Step N: Generate Images
|
||||
|
||||
**Skill Selection**:
|
||||
1. Check available image generation skills (e.g., `baoyu-danger-gemini-web`)
|
||||
1. Check available image generation skills (`baoyu-image-gen` default, or `baoyu-danger-gemini-web`)
|
||||
2. Read selected skill's SKILL.md for parameter reference
|
||||
3. If multiple skills available, ask user to choose
|
||||
|
||||
@@ -243,11 +402,14 @@ Use this template when implementing image generation in skills:
|
||||
- Prompt file path (or inline prompt)
|
||||
- Output image path
|
||||
- Any skill-specific parameters (refer to skill's SKILL.md)
|
||||
2. Generate images sequentially (one at a time)
|
||||
2. Generate images sequentially by default (one at a time)
|
||||
- Use batch parallel mode only when user already has multiple prompts or explicitly wants parallel throughput
|
||||
3. After each image, output progress: "Generated X/N"
|
||||
4. On failure, auto-retry once before reporting error
|
||||
```
|
||||
|
||||
**Batch Parallel Generation** (`baoyu-image-gen` only): supports concurrent workers with per-provider throttling. Configure via `batch.max_workers` and `batch.provider_limits` in EXTEND.md. Default: sequential.
|
||||
|
||||
### Output Path Convention
|
||||
|
||||
Each session creates an independent directory. Even the same source file generates a new directory per session.
|
||||
@@ -320,13 +482,13 @@ When adding, updating, or deleting styles for `baoyu-comic`, follow this workflo
|
||||
- Add auto-selection entry if applicable
|
||||
3. **Generate showcase image**:
|
||||
```bash
|
||||
npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts \
|
||||
${BUN_X} skills/baoyu-danger-gemini-web/scripts/main.ts \
|
||||
--prompt "A single comic book page in <style-name> style showing [appropriate scene]. Features: [style characteristics from style definition]. 3:4 portrait aspect ratio comic page." \
|
||||
--image screenshots/comic-styles/<style-name>.png
|
||||
```
|
||||
4. **Compress to WebP**:
|
||||
```bash
|
||||
npx -y bun skills/baoyu-compress-image/scripts/main.ts screenshots/comic-styles/<style-name>.png
|
||||
${BUN_X} skills/baoyu-compress-image/scripts/main.ts screenshots/comic-styles/<style-name>.png
|
||||
```
|
||||
5. **Update both READMEs** (`README.md` and `README.zh.md`):
|
||||
- Add style to `--style` options
|
||||
@@ -374,23 +536,32 @@ For skills with workflows, add as Step 1.1. For utility skills, add as "Preferen
|
||||
```markdown
|
||||
**1.1 Load Preferences (EXTEND.md)**
|
||||
|
||||
Use Bash to check EXTEND.md existence (priority order):
|
||||
Check EXTEND.md existence (priority order):
|
||||
|
||||
\`\`\`bash
|
||||
# Check project-level first
|
||||
# macOS, Linux, WSL, Git Bash
|
||||
test -f .baoyu-skills/<skill-name>/EXTEND.md && echo "project"
|
||||
|
||||
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/<skill-name>/EXTEND.md" && echo "xdg"
|
||||
test -f "$HOME/.baoyu-skills/<skill-name>/EXTEND.md" && echo "user"
|
||||
\`\`\`
|
||||
|
||||
┌────────────────────────────────────────────┬───────────────────┐
|
||||
│ Path │ Location │
|
||||
├────────────────────────────────────────────┼───────────────────┤
|
||||
│ .baoyu-skills/<skill-name>/EXTEND.md │ Project directory │
|
||||
├────────────────────────────────────────────┼───────────────────┤
|
||||
│ $HOME/.baoyu-skills/<skill-name>/EXTEND.md │ User home │
|
||||
└────────────────────────────────────────────┴───────────────────┘
|
||||
\`\`\`powershell
|
||||
# PowerShell (Windows)
|
||||
if (Test-Path .baoyu-skills/<skill-name>/EXTEND.md) { "project" }
|
||||
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
|
||||
if (Test-Path "$xdg/baoyu-skills/<skill-name>/EXTEND.md") { "xdg" }
|
||||
if (Test-Path "$HOME/.baoyu-skills/<skill-name>/EXTEND.md") { "user" }
|
||||
\`\`\`
|
||||
|
||||
┌────────────────────────────────────────────────────────┬──────────────────────────┐
|
||||
│ Path │ Location │
|
||||
├────────────────────────────────────────────────────────┼──────────────────────────┤
|
||||
│ .baoyu-skills/<skill-name>/EXTEND.md │ Project directory │
|
||||
├────────────────────────────────────────────────────────┼──────────────────────────┤
|
||||
│ $XDG_CONFIG_HOME/baoyu-skills/<skill-name>/EXTEND.md │ XDG config (~/.config) │
|
||||
├────────────────────────────────────────────────────────┼──────────────────────────┤
|
||||
│ $HOME/.baoyu-skills/<skill-name>/EXTEND.md │ User home (legacy) │
|
||||
└────────────────────────────────────────────────────────┴──────────────────────────┘
|
||||
|
||||
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
|
||||
│ Result │ Action │
|
||||
@@ -425,6 +596,7 @@ Custom configurations via EXTEND.md. See **Preferences** section for paths and s
|
||||
|
||||
**Notes**:
|
||||
- Replace `<skill-name>` with actual skill name (e.g., `baoyu-cover-image`)
|
||||
- Use `$HOME` instead of `~` for cross-platform compatibility (macOS/Linux/WSL)
|
||||
- Use `test -f` Bash command for explicit file existence check
|
||||
- Use `$HOME` instead of `~` for cross-platform compatibility (macOS/Linux/WSL/PowerShell)
|
||||
- `$XDG_CONFIG_HOME` defaults to `~/.config` when unset
|
||||
- Use `test -f` (Bash) or `Test-Path` (PowerShell) for explicit file existence check
|
||||
- ASCII tables for clear visual formatting
|
||||
|
||||
@@ -17,6 +17,27 @@ Skills shared by Baoyu for improving daily work efficiency with Claude Code.
|
||||
npx skills add jimliu/baoyu-skills
|
||||
```
|
||||
|
||||
### Publish to ClawHub / OpenClaw
|
||||
|
||||
This repository now supports publishing each `skills/baoyu-*` directory as an individual ClawHub skill.
|
||||
|
||||
```bash
|
||||
# Preview what would be published
|
||||
./scripts/sync-clawhub.sh --dry-run
|
||||
|
||||
# Publish all changed skills from ./skills
|
||||
./scripts/sync-clawhub.sh --all
|
||||
```
|
||||
|
||||
ClawHub installs skills individually, not as one marketplace bundle. After publishing, users can install specific skills such as:
|
||||
|
||||
```bash
|
||||
clawhub install baoyu-image-gen
|
||||
clawhub install baoyu-markdown-to-html
|
||||
```
|
||||
|
||||
Publishing to ClawHub releases the published skill under `MIT-0`, per ClawHub's registry rules.
|
||||
|
||||
### Register as Plugin Marketplace
|
||||
|
||||
Run the following command in Claude Code:
|
||||
@@ -53,9 +74,9 @@ Simply tell Claude Code:
|
||||
|
||||
| Plugin | Description | Skills |
|
||||
|--------|-------------|--------|
|
||||
| **content-skills** | Content generation and publishing | [xhs-images](#baoyu-xhs-images), [infographic](#baoyu-infographic), [cover-image](#baoyu-cover-image), [slide-deck](#baoyu-slide-deck), [comic](#baoyu-comic), [article-illustrator](#baoyu-article-illustrator), [post-to-x](#baoyu-post-to-x), [post-to-wechat](#baoyu-post-to-wechat) |
|
||||
| **content-skills** | Content generation and publishing | [xhs-images](#baoyu-xhs-images), [infographic](#baoyu-infographic), [cover-image](#baoyu-cover-image), [slide-deck](#baoyu-slide-deck), [comic](#baoyu-comic), [article-illustrator](#baoyu-article-illustrator), [post-to-x](#baoyu-post-to-x), [post-to-wechat](#baoyu-post-to-wechat), [post-to-weibo](#baoyu-post-to-weibo) |
|
||||
| **ai-generation-skills** | AI-powered generation backends | [image-gen](#baoyu-image-gen), [danger-gemini-web](#baoyu-danger-gemini-web) |
|
||||
| **utility-skills** | Utility tools for content processing | [url-to-markdown](#baoyu-url-to-markdown), [danger-x-to-markdown](#baoyu-danger-x-to-markdown), [compress-image](#baoyu-compress-image) |
|
||||
| **utility-skills** | Utility tools for content processing | [url-to-markdown](#baoyu-url-to-markdown), [danger-x-to-markdown](#baoyu-danger-x-to-markdown), [compress-image](#baoyu-compress-image), [format-markdown](#baoyu-format-markdown), [markdown-to-html](#baoyu-markdown-to-html), [translate](#baoyu-translate) |
|
||||
|
||||
## Update Skills
|
||||
|
||||
@@ -245,45 +266,35 @@ Generate professional infographics with 20 layout types and 17 visual styles. An
|
||||
|
||||
#### baoyu-cover-image
|
||||
|
||||
Generate cover images for articles with Type × Style two-dimension system.
|
||||
Generate cover images for articles with 5 dimensions: Type × Palette × Rendering × Text × Mood. Combines 9 color palettes with 6 rendering styles for 54 unique combinations.
|
||||
|
||||
```bash
|
||||
# Auto-select type and style based on content
|
||||
# Auto-select all dimensions based on content
|
||||
/baoyu-cover-image path/to/article.md
|
||||
|
||||
# Specify type and/or style
|
||||
/baoyu-cover-image path/to/article.md --type conceptual --style blueprint
|
||||
/baoyu-cover-image path/to/article.md --style warm
|
||||
# Quick mode: skip confirmation, use auto-selection
|
||||
/baoyu-cover-image path/to/article.md --quick
|
||||
|
||||
# Specify aspect ratio (default: 2.35:1)
|
||||
/baoyu-cover-image path/to/article.md --aspect 16:9
|
||||
# Specify dimensions (5D system)
|
||||
/baoyu-cover-image path/to/article.md --type conceptual --palette cool --rendering digital
|
||||
/baoyu-cover-image path/to/article.md --text title-subtitle --mood bold
|
||||
|
||||
# Without title text
|
||||
# Style presets (backward-compatible shorthand)
|
||||
/baoyu-cover-image path/to/article.md --style blueprint
|
||||
|
||||
# Specify aspect ratio (default: 16:9)
|
||||
/baoyu-cover-image path/to/article.md --aspect 2.35:1
|
||||
|
||||
# Visual only (no title text)
|
||||
/baoyu-cover-image path/to/article.md --no-title
|
||||
```
|
||||
|
||||
Available types: `hero`, `conceptual`, `typography`, `metaphor`, `scene`, `minimal`
|
||||
|
||||
Available styles: `elegant` (default), `blueprint`, `bold-editorial`, `chalkboard`, `dark-atmospheric`, `editorial-infographic`, `fantasy-animation`, `flat-doodle`, `intuition-machine`, `minimal`, `nature`, `notion`, `pixel-art`, `playful`, `retro`, `sketch-notes`, `vector-illustration`, `vintage`, `warm`, `watercolor`
|
||||
|
||||
**Style Previews**:
|
||||
|
||||
| | | |
|
||||
|:---:|:---:|:---:|
|
||||
|  |  |  |
|
||||
| elegant | blueprint | bold-editorial |
|
||||
|  |  |  |
|
||||
| chalkboard | dark-atmospheric | editorial-infographic |
|
||||
|  |  |  |
|
||||
| fantasy-animation | intuition-machine | minimal |
|
||||
|  |  |  |
|
||||
| nature | notion | pixel-art |
|
||||
|  |  |  |
|
||||
| playful | retro | sketch-notes |
|
||||
|  |  |  |
|
||||
| vector-illustration | vintage | warm |
|
||||
|  |  | |
|
||||
| watercolor | flat-doodle | |
|
||||
**Five Dimensions**:
|
||||
- **Type**: `hero`, `conceptual`, `typography`, `metaphor`, `scene`, `minimal`
|
||||
- **Palette**: `warm`, `elegant`, `cool`, `dark`, `earth`, `vivid`, `pastel`, `mono`, `retro`
|
||||
- **Rendering**: `flat-vector`, `hand-drawn`, `painterly`, `digital`, `pixel`, `chalk`
|
||||
- **Text**: `none`, `title-only` (default), `title-subtitle`, `text-rich`
|
||||
- **Mood**: `subtle`, `balanced` (default), `bold`
|
||||
|
||||
#### baoyu-slide-deck
|
||||
|
||||
@@ -514,6 +525,8 @@ Smart article illustration skill with Type × Style two-dimension approach. Anal
|
||||
|
||||
Post content and articles to X (Twitter). Supports regular posts with images and X Articles (long-form Markdown). Uses real Chrome with CDP to bypass anti-automation.
|
||||
|
||||
Plain text input is treated as a regular post. Markdown files are treated as X Articles. Scripts fill content into the browser, and the user reviews and publishes manually.
|
||||
|
||||
```bash
|
||||
# Post with text
|
||||
/baoyu-post-to-x "Hello from Claude Code!"
|
||||
@@ -529,12 +542,12 @@ Post content and articles to X (Twitter). Supports regular posts with images and
|
||||
|
||||
Post content to WeChat Official Account (微信公众号). Two modes available:
|
||||
|
||||
**Image-Text (图文)** - Multiple images with short title/content:
|
||||
**Image-Text (贴图)** - Multiple images with short title/content:
|
||||
|
||||
```bash
|
||||
/baoyu-post-to-wechat 图文 --markdown article.md --images ./photos/
|
||||
/baoyu-post-to-wechat 图文 --markdown article.md --image img1.png --image img2.png --image img3.png
|
||||
/baoyu-post-to-wechat 图文 --title "标题" --content "内容" --image img1.png --submit
|
||||
/baoyu-post-to-wechat 贴图 --markdown article.md --images ./photos/
|
||||
/baoyu-post-to-wechat 贴图 --markdown article.md --image img1.png --image img2.png --image img3.png
|
||||
/baoyu-post-to-wechat 贴图 --title "标题" --content "内容" --image img1.png --submit
|
||||
```
|
||||
|
||||
**Article (文章)** - Full markdown/HTML with rich formatting:
|
||||
@@ -545,7 +558,64 @@ Post content to WeChat Official Account (微信公众号). Two modes available:
|
||||
/baoyu-post-to-wechat 文章 --html article.html
|
||||
```
|
||||
|
||||
Prerequisites: Google Chrome installed. First run requires QR code login (session preserved).
|
||||
**Publishing Methods**:
|
||||
|
||||
| Method | Speed | Requirements |
|
||||
|--------|-------|--------------|
|
||||
| API (Recommended) | Fast | API credentials |
|
||||
| Browser | Slow | Chrome, login session |
|
||||
|
||||
**API Configuration** (for faster publishing):
|
||||
|
||||
```bash
|
||||
# Add to .baoyu-skills/.env (project-level) or ~/.baoyu-skills/.env (user-level)
|
||||
WECHAT_APP_ID=your_app_id
|
||||
WECHAT_APP_SECRET=your_app_secret
|
||||
```
|
||||
|
||||
To obtain credentials:
|
||||
1. Visit https://developers.weixin.qq.com/platform/
|
||||
2. Go to: 我的业务 → 公众号 → 开发密钥
|
||||
3. Create development key and copy AppID/AppSecret
|
||||
4. Add your machine's IP to the whitelist
|
||||
|
||||
**Browser Method** (no API setup needed): Requires Google Chrome. First run opens browser for QR code login (session preserved).
|
||||
|
||||
#### baoyu-post-to-weibo
|
||||
|
||||
Post content to Weibo (微博). Supports regular posts with text, images, and videos, and headline articles (头条文章) with Markdown input. Uses real Chrome with CDP to bypass anti-automation.
|
||||
|
||||
**Regular Posts** - Text + images/videos (max 18 files):
|
||||
|
||||
```bash
|
||||
# Post with text
|
||||
/baoyu-post-to-weibo "Hello Weibo!"
|
||||
|
||||
# Post with images
|
||||
/baoyu-post-to-weibo "Check this out" --image photo.png
|
||||
|
||||
# Post with video
|
||||
/baoyu-post-to-weibo "Watch this" --video clip.mp4
|
||||
```
|
||||
|
||||
**Headline Articles (头条文章)** - Long-form Markdown:
|
||||
|
||||
```bash
|
||||
# Publish article
|
||||
/baoyu-post-to-weibo --article article.md
|
||||
|
||||
# With cover image
|
||||
/baoyu-post-to-weibo --article article.md --cover cover.jpg
|
||||
```
|
||||
|
||||
**Article Options**:
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--cover <path>` | Cover image |
|
||||
| `--title <text>` | Override title (max 32 chars) |
|
||||
| `--summary <text>` | Override summary (max 44 chars) |
|
||||
|
||||
**Note**: Scripts fill content into the browser. User reviews and publishes manually. First run requires manual Weibo login (session persists).
|
||||
|
||||
### AI Generation Skills
|
||||
|
||||
@@ -553,7 +623,7 @@ AI-powered generation backends.
|
||||
|
||||
#### baoyu-image-gen
|
||||
|
||||
AI SDK-based image generation using official OpenAI and Google APIs. Supports text-to-image, reference images, aspect ratios, and quality presets.
|
||||
AI SDK-based image generation using official OpenAI, Google and DashScope (Aliyun Tongyi Wanxiang) APIs. Supports text-to-image, reference images, aspect ratios, and quality presets.
|
||||
|
||||
```bash
|
||||
# Basic generation (auto-detect provider)
|
||||
@@ -568,6 +638,9 @@ AI SDK-based image generation using official OpenAI and Google APIs. Supports te
|
||||
# Specific provider
|
||||
/baoyu-image-gen --prompt "A cat" --image cat.png --provider openai
|
||||
|
||||
# DashScope (Aliyun Tongyi Wanxiang)
|
||||
/baoyu-image-gen --prompt "一只可爱的猫" --image cat.png --provider dashscope
|
||||
|
||||
# With reference images (Google multimodal only)
|
||||
/baoyu-image-gen --prompt "Make it blue" --image out.png --ref source.png
|
||||
```
|
||||
@@ -578,7 +651,7 @@ AI SDK-based image generation using official OpenAI and Google APIs. Supports te
|
||||
| `--prompt`, `-p` | Prompt text |
|
||||
| `--promptfiles` | Read prompt from files (concatenated) |
|
||||
| `--image` | Output image path (required) |
|
||||
| `--provider` | `google` or `openai` (default: google) |
|
||||
| `--provider` | `google`, `openai` or `dashscope` (default: google) |
|
||||
| `--model`, `-m` | Model ID |
|
||||
| `--ar` | Aspect ratio (e.g., `16:9`, `1:1`, `4:3`) |
|
||||
| `--size` | Size (e.g., `1024x1024`) |
|
||||
@@ -590,15 +663,18 @@ AI SDK-based image generation using official OpenAI and Google APIs. Supports te
|
||||
|----------|-------------|---------|
|
||||
| `OPENAI_API_KEY` | OpenAI API key | - |
|
||||
| `GOOGLE_API_KEY` | Google API key | - |
|
||||
| `DASHSCOPE_API_KEY` | DashScope API key (Aliyun) | - |
|
||||
| `OPENAI_IMAGE_MODEL` | OpenAI model | `gpt-image-1.5` |
|
||||
| `GOOGLE_IMAGE_MODEL` | Google model | `gemini-3-pro-image-preview` |
|
||||
| `DASHSCOPE_IMAGE_MODEL` | DashScope model | `z-image-turbo` |
|
||||
| `OPENAI_BASE_URL` | Custom OpenAI endpoint | - |
|
||||
| `GOOGLE_BASE_URL` | Custom Google endpoint | - |
|
||||
| `DASHSCOPE_BASE_URL` | Custom DashScope endpoint | - |
|
||||
|
||||
**Provider Auto-Selection**:
|
||||
1. If `--provider` specified → use it
|
||||
2. If only one API key available → use that provider
|
||||
3. If both available → default to Google
|
||||
3. If multiple available → default to Google
|
||||
|
||||
#### baoyu-danger-gemini-web
|
||||
|
||||
@@ -624,7 +700,7 @@ Utility tools for content processing.
|
||||
|
||||
#### baoyu-url-to-markdown
|
||||
|
||||
Fetch any URL via Chrome CDP and convert to clean markdown. Supports two capture modes for different scenarios.
|
||||
Fetch any URL via Chrome CDP and convert to clean markdown. Saves rendered HTML snapshot alongside the markdown, and automatically falls back to a legacy extractor when Defuddle fails.
|
||||
|
||||
```bash
|
||||
# Auto mode (default) - capture when page loads
|
||||
@@ -664,6 +740,9 @@ Converts X (Twitter) content to markdown format. Supports tweet threads and X Ar
|
||||
|
||||
# JSON output
|
||||
/baoyu-danger-x-to-markdown https://x.com/username/status/123456 --json
|
||||
|
||||
# Download media (images/videos) to local files
|
||||
/baoyu-danger-x-to-markdown https://x.com/username/status/123456 --download-media
|
||||
```
|
||||
|
||||
**Supported URLs:**
|
||||
@@ -682,6 +761,138 @@ Compress images to reduce file size while maintaining quality.
|
||||
/baoyu-compress-image path/to/images/ --quality 80
|
||||
```
|
||||
|
||||
#### baoyu-format-markdown
|
||||
|
||||
Format plain text or markdown files with proper frontmatter, titles, summaries, headings, bold, lists, and code blocks.
|
||||
|
||||
```bash
|
||||
# Format a markdown file
|
||||
/baoyu-format-markdown path/to/article.md
|
||||
|
||||
# Format with specific output
|
||||
/baoyu-format-markdown path/to/draft.md
|
||||
```
|
||||
|
||||
**Workflow**:
|
||||
1. Read source file and analyze content structure
|
||||
2. Check/create YAML frontmatter (title, slug, summary, coverImage)
|
||||
3. Handle title: use existing, extract from H1, or generate candidates
|
||||
4. Apply formatting: headings, bold, lists, code blocks, quotes
|
||||
5. Save to `{filename}-formatted.md`
|
||||
6. Run typography script: ASCII→fullwidth quotes, CJK spacing, autocorrect
|
||||
|
||||
**Frontmatter Fields**:
|
||||
| Field | Processing |
|
||||
|-------|------------|
|
||||
| `title` | Use existing, extract H1, or generate candidates |
|
||||
| `slug` | Infer from file path or generate from title |
|
||||
| `summary` | Generate engaging summary (100-150 chars) |
|
||||
| `coverImage` | Check for `imgs/cover.png` in same directory |
|
||||
|
||||
**Formatting Rules**:
|
||||
| Element | Format |
|
||||
|---------|--------|
|
||||
| Titles | `#`, `##`, `###` hierarchy |
|
||||
| Key points | `**bold**` |
|
||||
| Parallel items | `-` unordered or `1.` ordered lists |
|
||||
| Code/commands | `` `inline` `` or ` ```block``` ` |
|
||||
| Quotes | `>` blockquote |
|
||||
|
||||
#### baoyu-markdown-to-html
|
||||
|
||||
Convert markdown files into styled HTML with WeChat-compatible themes, syntax highlighting, and optional bottom citations for external links.
|
||||
|
||||
```bash
|
||||
# Basic conversion
|
||||
/baoyu-markdown-to-html article.md
|
||||
|
||||
# Theme + color
|
||||
/baoyu-markdown-to-html article.md --theme grace --color red
|
||||
|
||||
# Convert ordinary external links to bottom citations
|
||||
/baoyu-markdown-to-html article.md --cite
|
||||
```
|
||||
|
||||
#### baoyu-translate
|
||||
|
||||
Translate articles and documents between languages with three modes: quick (direct), normal (analysis-informed), and refined (full publication-quality workflow with review and polish).
|
||||
|
||||
```bash
|
||||
# Normal mode (default) - analyze then translate
|
||||
/translate article.md --to zh-CN
|
||||
|
||||
# Quick mode - direct translation
|
||||
/translate article.md --mode quick --to ja
|
||||
|
||||
# Refined mode - full workflow with review and polish
|
||||
/translate article.md --mode refined --to zh-CN
|
||||
|
||||
# Translate a URL
|
||||
/translate https://example.com/article --to zh-CN
|
||||
|
||||
# Specify audience
|
||||
/translate article.md --to zh-CN --audience technical
|
||||
|
||||
# Specify style
|
||||
/translate article.md --to zh-CN --style humorous
|
||||
|
||||
# With additional glossary
|
||||
/translate article.md --to zh-CN --glossary my-terms.md
|
||||
```
|
||||
|
||||
**Options**:
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `<source>` | File path, URL, or inline text |
|
||||
| `--mode <mode>` | `quick`, `normal` (default), `refined` |
|
||||
| `--from <lang>` | Source language (auto-detect if omitted) |
|
||||
| `--to <lang>` | Target language (default: `zh-CN`) |
|
||||
| `--audience <type>` | Target reader profile (default: `general`) |
|
||||
| `--style <style>` | Translation style (default: `storytelling`) |
|
||||
| `--glossary <file>` | Additional glossary file |
|
||||
|
||||
**Modes**:
|
||||
| Mode | Steps | Use Case |
|
||||
|------|-------|----------|
|
||||
| Quick | Translate | Short texts, informal content |
|
||||
| Normal | Analyze → Translate | Articles, blog posts |
|
||||
| Refined | Analyze → Translate → Review → Polish | Publication-quality documents |
|
||||
|
||||
After normal mode completes, you can reply "继续润色" or "refine" to continue with review and polish steps.
|
||||
|
||||
**Audience Presets**:
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `general` | General readers (default) — plain language, more translator's notes |
|
||||
| `technical` | Developers / engineers — less annotation on common tech terms |
|
||||
| `academic` | Researchers / scholars — formal register, precise terminology |
|
||||
| `business` | Business professionals — business-friendly tone |
|
||||
|
||||
Custom audience descriptions are also accepted, e.g., `--audience "AI-interested general readers"`.
|
||||
|
||||
**Style Presets**:
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `storytelling` | Engaging narrative flow (default) — smooth transitions, vivid phrasing |
|
||||
| `formal` | Professional, structured — neutral tone, no colloquialisms |
|
||||
| `technical` | Precise, documentation-style — concise, terminology-heavy |
|
||||
| `literal` | Close to original structure — minimal restructuring |
|
||||
| `academic` | Scholarly, rigorous — formal register, complex clauses OK |
|
||||
| `business` | Concise, results-focused — action-oriented, executive-friendly |
|
||||
| `humorous` | Preserves and adapts humor — witty, recreates comedic effect |
|
||||
| `conversational` | Casual, spoken-like — friendly, as if explaining to a friend |
|
||||
| `elegant` | Literary, polished prose — aesthetically refined, carefully crafted |
|
||||
|
||||
Custom style descriptions are also accepted, e.g., `--style "poetic and lyrical"`.
|
||||
|
||||
**Features**:
|
||||
- Custom glossaries via EXTEND.md with built-in EN→ZH glossary
|
||||
- Audience-aware translation with adjustable annotation depth
|
||||
- Automatic chunking for long documents (4000+ words) with parallel subagent translation
|
||||
- Figurative language interpreted by meaning, not word-for-word
|
||||
- Translator's notes for cultural/domain-specific references
|
||||
- Output directory with all intermediate files preserved
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
Some skills require API keys or custom configuration. Environment variables can be set in `.env` files:
|
||||
@@ -709,6 +920,11 @@ OPENAI_IMAGE_MODEL=gpt-image-1.5
|
||||
GOOGLE_API_KEY=xxx
|
||||
GOOGLE_IMAGE_MODEL=gemini-3-pro-image-preview
|
||||
# GOOGLE_BASE_URL=https://generativelanguage.googleapis.com/v1beta
|
||||
|
||||
# DashScope (Aliyun Tongyi Wanxiang)
|
||||
DASHSCOPE_API_KEY=sk-xxx
|
||||
DASHSCOPE_IMAGE_MODEL=z-image-turbo
|
||||
# DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/api/v1
|
||||
EOF
|
||||
```
|
||||
|
||||
@@ -737,13 +953,14 @@ mkdir -p .baoyu-skills/baoyu-cover-image
|
||||
Then create `.baoyu-skills/baoyu-cover-image/EXTEND.md`:
|
||||
|
||||
```markdown
|
||||
## Custom Styles
|
||||
## Custom Palettes
|
||||
|
||||
### brand
|
||||
- Primary color: #1a73e8
|
||||
- Secondary color: #34a853
|
||||
- Font style: Modern sans-serif
|
||||
- Always include company logo watermark
|
||||
### corporate-tech
|
||||
- Primary colors: #1a73e8, #4A90D9
|
||||
- Background: #F5F7FA
|
||||
- Accent colors: #00B4D8, #48CAE4
|
||||
- Decorative hints: Clean lines, subtle gradients
|
||||
- Best for: SaaS, enterprise, technical
|
||||
```
|
||||
|
||||
The extension content will be loaded before skill execution and override defaults.
|
||||
@@ -779,6 +996,19 @@ This skill uses a reverse-engineered X (Twitter) API.
|
||||
- First use requires consent acknowledgment
|
||||
- Authentication via environment variables or Chrome login
|
||||
|
||||
## Credits
|
||||
|
||||
This project was inspired by and builds upon the following open source projects:
|
||||
|
||||
- [x-article-publisher-skill](https://github.com/wshuyi/x-article-publisher-skill) by [@wshuyi](https://github.com/wshuyi) — Inspiration for the X article publishing skill
|
||||
- [doocs/md](https://github.com/doocs/md) by [@doocs](https://github.com/doocs) — Core implementation logic for Markdown to HTML conversion
|
||||
- [High-density Infographic Prompt](https://waytoagi.feishu.cn/wiki/YG0zwalijihRREkgmPzcWRInnUg) by AJ@WaytoAGI — Inspiration for the infographic skill
|
||||
- [qiaomu-mondo-poster-design](https://github.com/joeseesun/qiaomu-mondo-poster-design) by [@joeseesun](https://github.com/joeseesun)(乔木) — Inspiration for the Mondo style
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#JimLiu/baoyu-skills&Date)
|
||||
|
||||
+277
-47
@@ -17,6 +17,27 @@
|
||||
npx skills add jimliu/baoyu-skills
|
||||
```
|
||||
|
||||
### 发布到 ClawHub / OpenClaw
|
||||
|
||||
现在这个仓库支持把每个 `skills/baoyu-*` 目录作为独立 ClawHub skill 发布。
|
||||
|
||||
```bash
|
||||
# 预览将要发布的变更
|
||||
./scripts/sync-clawhub.sh --dry-run
|
||||
|
||||
# 发布 ./skills 下所有已变更的 skill
|
||||
./scripts/sync-clawhub.sh --all
|
||||
```
|
||||
|
||||
ClawHub 按“单个 skill”安装,不是把整个 marketplace 一次性装进去。发布后,用户可以按需安装:
|
||||
|
||||
```bash
|
||||
clawhub install baoyu-image-gen
|
||||
clawhub install baoyu-markdown-to-html
|
||||
```
|
||||
|
||||
根据 ClawHub 的 registry 规则,发布到 ClawHub 的 skill 会以 `MIT-0` 许可分发。
|
||||
|
||||
### 注册插件市场
|
||||
|
||||
在 Claude Code 中运行:
|
||||
@@ -53,9 +74,9 @@ npx skills add jimliu/baoyu-skills
|
||||
|
||||
| 插件 | 说明 | 包含技能 |
|
||||
|------|------|----------|
|
||||
| **content-skills** | 内容生成和发布 | [xhs-images](#baoyu-xhs-images), [infographic](#baoyu-infographic), [cover-image](#baoyu-cover-image), [slide-deck](#baoyu-slide-deck), [comic](#baoyu-comic), [article-illustrator](#baoyu-article-illustrator), [post-to-x](#baoyu-post-to-x), [post-to-wechat](#baoyu-post-to-wechat) |
|
||||
| **content-skills** | 内容生成和发布 | [xhs-images](#baoyu-xhs-images), [infographic](#baoyu-infographic), [cover-image](#baoyu-cover-image), [slide-deck](#baoyu-slide-deck), [comic](#baoyu-comic), [article-illustrator](#baoyu-article-illustrator), [post-to-x](#baoyu-post-to-x), [post-to-wechat](#baoyu-post-to-wechat), [post-to-weibo](#baoyu-post-to-weibo) |
|
||||
| **ai-generation-skills** | AI 生成后端 | [image-gen](#baoyu-image-gen), [danger-gemini-web](#baoyu-danger-gemini-web) |
|
||||
| **utility-skills** | 内容处理工具 | [url-to-markdown](#baoyu-url-to-markdown), [danger-x-to-markdown](#baoyu-danger-x-to-markdown), [compress-image](#baoyu-compress-image) |
|
||||
| **utility-skills** | 内容处理工具 | [url-to-markdown](#baoyu-url-to-markdown), [danger-x-to-markdown](#baoyu-danger-x-to-markdown), [compress-image](#baoyu-compress-image), [format-markdown](#baoyu-format-markdown), [markdown-to-html](#baoyu-markdown-to-html), [translate](#baoyu-translate) |
|
||||
|
||||
## 更新技能
|
||||
|
||||
@@ -245,45 +266,35 @@ npx skills add jimliu/baoyu-skills
|
||||
|
||||
#### baoyu-cover-image
|
||||
|
||||
为文章生成封面图,支持类型 × 风格二维系统。
|
||||
为文章生成封面图,支持五维定制系统:类型 × 配色 × 渲染 × 文字 × 氛围。9 种配色方案与 6 种渲染风格组合,提供 54 种独特效果。
|
||||
|
||||
```bash
|
||||
# 根据内容自动选择类型和风格
|
||||
# 根据内容自动选择所有维度
|
||||
/baoyu-cover-image path/to/article.md
|
||||
|
||||
# 指定类型和/或风格
|
||||
/baoyu-cover-image path/to/article.md --type conceptual --style blueprint
|
||||
/baoyu-cover-image path/to/article.md --style warm
|
||||
# 快速模式:跳过确认,使用自动选择
|
||||
/baoyu-cover-image path/to/article.md --quick
|
||||
|
||||
# 指定宽高比(默认:2.35:1)
|
||||
/baoyu-cover-image path/to/article.md --aspect 16:9
|
||||
# 指定维度(5D 系统)
|
||||
/baoyu-cover-image path/to/article.md --type conceptual --palette cool --rendering digital
|
||||
/baoyu-cover-image path/to/article.md --text title-subtitle --mood bold
|
||||
|
||||
# 不包含标题文字
|
||||
# 风格预设(向后兼容的简写方式)
|
||||
/baoyu-cover-image path/to/article.md --style blueprint
|
||||
|
||||
# 指定宽高比(默认:16:9)
|
||||
/baoyu-cover-image path/to/article.md --aspect 2.35:1
|
||||
|
||||
# 纯视觉(不含标题文字)
|
||||
/baoyu-cover-image path/to/article.md --no-title
|
||||
```
|
||||
|
||||
可用类型:`hero`、`conceptual`、`typography`、`metaphor`、`scene`、`minimal`
|
||||
|
||||
可用风格:`elegant`(默认)、`blueprint`、`bold-editorial`、`chalkboard`、`dark-atmospheric`、`editorial-infographic`、`fantasy-animation`、`flat-doodle`、`intuition-machine`、`minimal`、`nature`、`notion`、`pixel-art`、`playful`、`retro`、`sketch-notes`、`vector-illustration`、`vintage`、`warm`、`watercolor`
|
||||
|
||||
**风格预览**:
|
||||
|
||||
| | | |
|
||||
|:---:|:---:|:---:|
|
||||
|  |  |  |
|
||||
| elegant | blueprint | bold-editorial |
|
||||
|  |  |  |
|
||||
| chalkboard | dark-atmospheric | editorial-infographic |
|
||||
|  |  |  |
|
||||
| fantasy-animation | intuition-machine | minimal |
|
||||
|  |  |  |
|
||||
| nature | notion | pixel-art |
|
||||
|  |  |  |
|
||||
| playful | retro | sketch-notes |
|
||||
|  |  |  |
|
||||
| vector-illustration | vintage | warm |
|
||||
|  |  | |
|
||||
| watercolor | flat-doodle | |
|
||||
**五个维度**:
|
||||
- **类型 (Type)**:`hero`、`conceptual`、`typography`、`metaphor`、`scene`、`minimal`
|
||||
- **配色 (Palette)**:`warm`、`elegant`、`cool`、`dark`、`earth`、`vivid`、`pastel`、`mono`、`retro`
|
||||
- **渲染 (Rendering)**:`flat-vector`、`hand-drawn`、`painterly`、`digital`、`pixel`、`chalk`
|
||||
- **文字 (Text)**:`none`、`title-only`(默认)、`title-subtitle`、`text-rich`
|
||||
- **氛围 (Mood)**:`subtle`、`balanced`(默认)、`bold`
|
||||
|
||||
#### baoyu-slide-deck
|
||||
|
||||
@@ -514,6 +525,8 @@ npx skills add jimliu/baoyu-skills
|
||||
|
||||
发布内容和文章到 X (Twitter)。支持带图片的普通帖子和 X 文章(长篇 Markdown)。使用真实 Chrome + CDP 绕过反自动化检测。
|
||||
|
||||
纯文本输入默认按普通帖子处理,Markdown 文件默认按 X 文章处理。脚本会将内容填入浏览器,用户需手动检查并发布。
|
||||
|
||||
```bash
|
||||
# 发布文字
|
||||
/baoyu-post-to-x "Hello from Claude Code!"
|
||||
@@ -529,12 +542,12 @@ npx skills add jimliu/baoyu-skills
|
||||
|
||||
发布内容到微信公众号,支持两种模式:
|
||||
|
||||
**图文模式** - 多图配短标题和正文:
|
||||
**贴图模式** - 多图配短标题和正文:
|
||||
|
||||
```bash
|
||||
/baoyu-post-to-wechat 图文 --markdown article.md --images ./photos/
|
||||
/baoyu-post-to-wechat 图文 --markdown article.md --image img1.png --image img2.png --image img3.png
|
||||
/baoyu-post-to-wechat 图文 --title "标题" --content "内容" --image img1.png --submit
|
||||
/baoyu-post-to-wechat 贴图 --markdown article.md --images ./photos/
|
||||
/baoyu-post-to-wechat 贴图 --markdown article.md --image img1.png --image img2.png --image img3.png
|
||||
/baoyu-post-to-wechat 贴图 --title "标题" --content "内容" --image img1.png --submit
|
||||
```
|
||||
|
||||
**文章模式** - 完整 markdown/HTML 富文本格式:
|
||||
@@ -545,7 +558,64 @@ npx skills add jimliu/baoyu-skills
|
||||
/baoyu-post-to-wechat 文章 --html article.html
|
||||
```
|
||||
|
||||
前置要求:已安装 Google Chrome,首次运行需扫码登录(登录状态会保存)
|
||||
**发布方式**:
|
||||
|
||||
| 方式 | 速度 | 要求 |
|
||||
|------|------|------|
|
||||
| API(推荐) | 快 | API 凭证 |
|
||||
| 浏览器 | 慢 | Chrome,登录会话 |
|
||||
|
||||
**API 配置**(更快的发布方式):
|
||||
|
||||
```bash
|
||||
# 添加到 .baoyu-skills/.env(项目级)或 ~/.baoyu-skills/.env(用户级)
|
||||
WECHAT_APP_ID=你的AppID
|
||||
WECHAT_APP_SECRET=你的AppSecret
|
||||
```
|
||||
|
||||
获取凭证方法:
|
||||
1. 访问 https://developers.weixin.qq.com/platform/
|
||||
2. 进入:我的业务 → 公众号 → 开发密钥
|
||||
3. 添加开发密钥,复制 AppID 和 AppSecret
|
||||
4. 将你操作的机器 IP 加入白名单
|
||||
|
||||
**浏览器方式**(无需 API 配置):需已安装 Google Chrome,首次运行需扫码登录(登录状态会保存)
|
||||
|
||||
#### baoyu-post-to-weibo
|
||||
|
||||
发布内容到微博。支持文字、图片、视频发布和头条文章(长篇 Markdown)。使用真实 Chrome + CDP 绕过反自动化检测。
|
||||
|
||||
**普通微博** - 文字 + 图片/视频(最多 18 个文件):
|
||||
|
||||
```bash
|
||||
# 发布文字
|
||||
/baoyu-post-to-weibo "Hello Weibo!"
|
||||
|
||||
# 发布带图片
|
||||
/baoyu-post-to-weibo "看看这个" --image photo.png
|
||||
|
||||
# 发布带视频
|
||||
/baoyu-post-to-weibo "看这个" --video clip.mp4
|
||||
```
|
||||
|
||||
**头条文章** - 长篇 Markdown 文章:
|
||||
|
||||
```bash
|
||||
# 发布文章
|
||||
/baoyu-post-to-weibo --article article.md
|
||||
|
||||
# 带封面图
|
||||
/baoyu-post-to-weibo --article article.md --cover cover.jpg
|
||||
```
|
||||
|
||||
**文章选项**:
|
||||
| 选项 | 说明 |
|
||||
|------|------|
|
||||
| `--cover <path>` | 封面图 |
|
||||
| `--title <text>` | 覆盖标题(最多 32 字) |
|
||||
| `--summary <text>` | 覆盖摘要(最多 44 字) |
|
||||
|
||||
**说明**:脚本会将内容填入浏览器,用户需手动检查并发布。首次运行需手动登录微博(登录状态会保存)。
|
||||
|
||||
### AI 生成技能 (AI Generation Skills)
|
||||
|
||||
@@ -553,7 +623,7 @@ AI 驱动的生成后端。
|
||||
|
||||
#### baoyu-image-gen
|
||||
|
||||
基于 AI SDK 的图像生成,使用官方 OpenAI 和 Google API。支持文生图、参考图、宽高比和质量预设。
|
||||
基于 AI SDK 的图像生成,使用官方 OpenAI、Google 和 DashScope(阿里通义万相)API。支持文生图、参考图、宽高比和质量预设。
|
||||
|
||||
```bash
|
||||
# 基础生成(自动检测服务商)
|
||||
@@ -568,6 +638,9 @@ AI 驱动的生成后端。
|
||||
# 指定服务商
|
||||
/baoyu-image-gen --prompt "一只猫" --image cat.png --provider openai
|
||||
|
||||
# DashScope(阿里通义万相)
|
||||
/baoyu-image-gen --prompt "一只可爱的猫" --image cat.png --provider dashscope
|
||||
|
||||
# 带参考图(仅 Google 多模态支持)
|
||||
/baoyu-image-gen --prompt "把它变成蓝色" --image out.png --ref source.png
|
||||
```
|
||||
@@ -578,7 +651,7 @@ AI 驱动的生成后端。
|
||||
| `--prompt`, `-p` | 提示词文本 |
|
||||
| `--promptfiles` | 从文件读取提示词(多文件拼接) |
|
||||
| `--image` | 输出图片路径(必需) |
|
||||
| `--provider` | `google` 或 `openai`(默认:google) |
|
||||
| `--provider` | `google`、`openai` 或 `dashscope`(默认:google) |
|
||||
| `--model`, `-m` | 模型 ID |
|
||||
| `--ar` | 宽高比(如 `16:9`、`1:1`、`4:3`) |
|
||||
| `--size` | 尺寸(如 `1024x1024`) |
|
||||
@@ -590,15 +663,18 @@ AI 驱动的生成后端。
|
||||
|------|------|--------|
|
||||
| `OPENAI_API_KEY` | OpenAI API 密钥 | - |
|
||||
| `GOOGLE_API_KEY` | Google API 密钥 | - |
|
||||
| `DASHSCOPE_API_KEY` | DashScope API 密钥(阿里云) | - |
|
||||
| `OPENAI_IMAGE_MODEL` | OpenAI 模型 | `gpt-image-1.5` |
|
||||
| `GOOGLE_IMAGE_MODEL` | Google 模型 | `gemini-3-pro-image-preview` |
|
||||
| `DASHSCOPE_IMAGE_MODEL` | DashScope 模型 | `z-image-turbo` |
|
||||
| `OPENAI_BASE_URL` | 自定义 OpenAI 端点 | - |
|
||||
| `GOOGLE_BASE_URL` | 自定义 Google 端点 | - |
|
||||
| `DASHSCOPE_BASE_URL` | 自定义 DashScope 端点 | - |
|
||||
|
||||
**服务商自动选择**:
|
||||
1. 如果指定了 `--provider` → 使用指定的
|
||||
2. 如果只有一个 API 密钥 → 使用对应服务商
|
||||
3. 如果两个都有 → 默认使用 Google
|
||||
3. 如果多个可用 → 默认使用 Google
|
||||
|
||||
#### baoyu-danger-gemini-web
|
||||
|
||||
@@ -624,7 +700,7 @@ AI 驱动的生成后端。
|
||||
|
||||
#### baoyu-url-to-markdown
|
||||
|
||||
通过 Chrome CDP 抓取任意 URL 并转换为干净的 Markdown。支持两种抓取模式,适应不同场景。
|
||||
通过 Chrome CDP 抓取任意 URL 并转换为 Markdown。同时保存渲染后的 HTML 快照,Defuddle 失败时自动回退到旧版提取器。
|
||||
|
||||
```bash
|
||||
# 自动模式(默认)- 页面加载后立即抓取
|
||||
@@ -664,6 +740,9 @@ AI 驱动的生成后端。
|
||||
|
||||
# JSON 输出
|
||||
/baoyu-danger-x-to-markdown https://x.com/username/status/123456 --json
|
||||
|
||||
# 下载媒体文件(图片/视频)到本地
|
||||
/baoyu-danger-x-to-markdown https://x.com/username/status/123456 --download-media
|
||||
```
|
||||
|
||||
**支持的 URL:**
|
||||
@@ -682,6 +761,138 @@ AI 驱动的生成后端。
|
||||
/baoyu-compress-image path/to/images/ --quality 80
|
||||
```
|
||||
|
||||
#### baoyu-format-markdown
|
||||
|
||||
格式化纯文本或 Markdown 文件,添加 frontmatter、标题、摘要、层级标题、加粗、列表和代码块。
|
||||
|
||||
```bash
|
||||
# 格式化 markdown 文件
|
||||
/baoyu-format-markdown path/to/article.md
|
||||
|
||||
# 格式化指定文件
|
||||
/baoyu-format-markdown path/to/draft.md
|
||||
```
|
||||
|
||||
**工作流程**:
|
||||
1. 读取源文件并分析内容结构
|
||||
2. 检查/创建 YAML frontmatter(title、slug、summary、coverImage)
|
||||
3. 处理标题:使用现有标题、提取 H1 或生成候选标题
|
||||
4. 应用格式:层级标题、加粗、列表、代码块、引用
|
||||
5. 保存为 `{文件名}-formatted.md`
|
||||
6. 运行排版脚本:半角引号→全角引号、中英文空格、autocorrect
|
||||
|
||||
**Frontmatter 字段**:
|
||||
| 字段 | 处理方式 |
|
||||
|------|----------|
|
||||
| `title` | 使用现有、提取 H1 或生成候选 |
|
||||
| `slug` | 从文件路径推断或根据标题生成 |
|
||||
| `summary` | 生成吸引人的摘要(100-150 字) |
|
||||
| `coverImage` | 检查同目录下 `imgs/cover.png` |
|
||||
|
||||
**格式化规则**:
|
||||
| 元素 | 格式 |
|
||||
|------|------|
|
||||
| 标题 | `#`、`##`、`###` 层级 |
|
||||
| 重点内容 | `**加粗**` |
|
||||
| 并列要点 | `-` 无序列表或 `1.` 有序列表 |
|
||||
| 代码/命令 | `` `行内` `` 或 ` ```代码块``` ` |
|
||||
| 引用 | `>` 引用块 |
|
||||
|
||||
#### baoyu-markdown-to-html
|
||||
|
||||
将 Markdown 文件转换为样式化 HTML,支持微信公众号兼容主题、代码高亮,以及可选的外链底部引用。
|
||||
|
||||
```bash
|
||||
# 基础转换
|
||||
/baoyu-markdown-to-html article.md
|
||||
|
||||
# 主题 + 颜色
|
||||
/baoyu-markdown-to-html article.md --theme grace --color red
|
||||
|
||||
# 将普通外链转换为文末引用
|
||||
/baoyu-markdown-to-html article.md --cite
|
||||
```
|
||||
|
||||
#### baoyu-translate
|
||||
|
||||
三模式翻译技能:快速(直接翻译)、标准(分析后翻译)、精翻(完整出版级工作流,含审校与润色)。
|
||||
|
||||
```bash
|
||||
# 标准模式(默认)- 先分析再翻译
|
||||
/translate article.md --to zh-CN
|
||||
|
||||
# 快速模式 - 直接翻译
|
||||
/translate article.md --mode quick --to ja
|
||||
|
||||
# 精翻模式 - 完整工作流,含审校与润色
|
||||
/translate article.md --mode refined --to zh-CN
|
||||
|
||||
# 翻译 URL
|
||||
/translate https://example.com/article --to zh-CN
|
||||
|
||||
# 指定受众
|
||||
/translate article.md --to zh-CN --audience technical
|
||||
|
||||
# 指定风格
|
||||
/translate article.md --to zh-CN --style humorous
|
||||
|
||||
# 附加术语表
|
||||
/translate article.md --to zh-CN --glossary my-terms.md
|
||||
```
|
||||
|
||||
**选项**:
|
||||
| 选项 | 说明 |
|
||||
|------|------|
|
||||
| `<source>` | 文件路径、URL 或行内文本 |
|
||||
| `--mode <mode>` | `quick`、`normal`(默认)、`refined` |
|
||||
| `--from <lang>` | 源语言(省略则自动检测) |
|
||||
| `--to <lang>` | 目标语言(默认:`zh-CN`) |
|
||||
| `--audience <type>` | 目标读者(默认:`general`) |
|
||||
| `--style <style>` | 翻译风格(默认:`storytelling`) |
|
||||
| `--glossary <file>` | 附加术语表文件 |
|
||||
|
||||
**模式**:
|
||||
| 模式 | 步骤 | 适用场景 |
|
||||
|------|------|----------|
|
||||
| 快速 | 翻译 | 短文本、非正式内容 |
|
||||
| 标准 | 分析 → 翻译 | 文章、博客 |
|
||||
| 精翻 | 分析 → 翻译 → 审校 → 润色 | 出版级文档 |
|
||||
|
||||
标准模式完成后,可回复「继续润色」或「refine」继续审校润色步骤。
|
||||
|
||||
**受众预设**:
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| `general` | 普通读者(默认)— 通俗语言,更多译注 |
|
||||
| `technical` | 开发者/工程师 — 常见技术术语少加注释 |
|
||||
| `academic` | 研究者/学者 — 正式语体,精确术语 |
|
||||
| `business` | 商务人士 — 商务友好语气 |
|
||||
|
||||
也支持自定义受众描述,如 `--audience "对 AI 感兴趣的普通读者"`。
|
||||
|
||||
**风格预设**:
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| `storytelling` | 叙事流畅(默认)— 过渡自然,表达生动 |
|
||||
| `formal` | 正式、结构化 — 中性语气,无口语化表达 |
|
||||
| `technical` | 精确、文档风格 — 简洁,术语密集 |
|
||||
| `literal` | 贴近原文结构 — 最小化重构 |
|
||||
| `academic` | 学术、严谨 — 正式语体,复杂从句可接受 |
|
||||
| `business` | 简洁、结果导向 — 行动导向,高管友好 |
|
||||
| `humorous` | 保留幽默感 — 诙谐,在目标语言中重现喜剧效果 |
|
||||
| `conversational` | 口语化、亲切 — 友好,如同朋友间解释 |
|
||||
| `elegant` | 文学性、优雅 — 精心雕琢,注重韵律美感 |
|
||||
|
||||
也支持自定义风格描述,如 `--style "诗意而抒情"`。
|
||||
|
||||
**特性**:
|
||||
- 通过 EXTEND.md 自定义术语表,内置英中术语表
|
||||
- 面向受众的翻译,可调节注释深度
|
||||
- 长文档(4000+ 词)自动分块并行翻译
|
||||
- 比喻和修辞按意译而非逐字翻译
|
||||
- 为文化/专业术语添加译注
|
||||
- 输出目录保留所有中间文件
|
||||
|
||||
## 环境配置
|
||||
|
||||
部分技能需要 API 密钥或自定义配置。环境变量可以在 `.env` 文件中设置:
|
||||
@@ -709,6 +920,11 @@ OPENAI_IMAGE_MODEL=gpt-image-1.5
|
||||
GOOGLE_API_KEY=xxx
|
||||
GOOGLE_IMAGE_MODEL=gemini-3-pro-image-preview
|
||||
# GOOGLE_BASE_URL=https://generativelanguage.googleapis.com/v1beta
|
||||
|
||||
# DashScope(阿里通义万相)
|
||||
DASHSCOPE_API_KEY=sk-xxx
|
||||
DASHSCOPE_IMAGE_MODEL=z-image-turbo
|
||||
# DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/api/v1
|
||||
EOF
|
||||
```
|
||||
|
||||
@@ -737,13 +953,14 @@ mkdir -p .baoyu-skills/baoyu-cover-image
|
||||
然后创建 `.baoyu-skills/baoyu-cover-image/EXTEND.md`:
|
||||
|
||||
```markdown
|
||||
## 自定义风格
|
||||
## 自定义配色
|
||||
|
||||
### brand
|
||||
- 主色:#1a73e8
|
||||
- 辅色:#34a853
|
||||
- 字体风格:现代无衬线
|
||||
- 始终包含公司 logo 水印
|
||||
### corporate-tech
|
||||
- 主色:#1a73e8、#4A90D9
|
||||
- 背景色:#F5F7FA
|
||||
- 强调色:#00B4D8、#48CAE4
|
||||
- 装饰提示:简洁线条、渐变效果
|
||||
- 适用于:SaaS、企业、技术内容
|
||||
```
|
||||
|
||||
扩展内容会在技能执行前加载,并覆盖默认设置。
|
||||
@@ -779,6 +996,19 @@ HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 /baoyu-danger
|
||||
- 首次使用需确认免责声明
|
||||
- 通过环境变量或 Chrome 登录进行身份验证
|
||||
|
||||
## 致谢
|
||||
|
||||
本项目受到以下开源项目的启发,感谢它们的作者:
|
||||
|
||||
- [x-article-publisher-skill](https://github.com/wshuyi/x-article-publisher-skill) by [@wshuyi](https://github.com/wshuyi) — 发布 X 文章技能的灵感来源
|
||||
- [doocs/md](https://github.com/doocs/md) by [@doocs](https://github.com/doocs) — Markdown 转 HTML 的核心实现逻辑
|
||||
- [高密度信息图 Prompt](https://waytoagi.feishu.cn/wiki/YG0zwalijihRREkgmPzcWRInnUg) by AJ@WaytoAGI — 信息图技能的灵感来源
|
||||
- [qiaomu-mondo-poster-design](https://github.com/joeseesun/qiaomu-mondo-poster-design) by [@joeseesun](https://github.com/joeseesun)(乔木) — Mondo 风格的灵感来源
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#JimLiu/baoyu-skills&Date)
|
||||
|
||||
@@ -0,0 +1,528 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs/promises";
|
||||
import { existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import os from "node:os";
|
||||
|
||||
const DEFAULT_REGISTRY = "https://clawhub.ai";
|
||||
const TEXT_EXTENSIONS = new Set([
|
||||
"md",
|
||||
"mdx",
|
||||
"txt",
|
||||
"json",
|
||||
"json5",
|
||||
"yaml",
|
||||
"yml",
|
||||
"toml",
|
||||
"js",
|
||||
"cjs",
|
||||
"mjs",
|
||||
"ts",
|
||||
"tsx",
|
||||
"jsx",
|
||||
"py",
|
||||
"sh",
|
||||
"rb",
|
||||
"go",
|
||||
"rs",
|
||||
"swift",
|
||||
"kt",
|
||||
"java",
|
||||
"cs",
|
||||
"cpp",
|
||||
"c",
|
||||
"h",
|
||||
"hpp",
|
||||
"sql",
|
||||
"csv",
|
||||
"ini",
|
||||
"cfg",
|
||||
"env",
|
||||
"xml",
|
||||
"html",
|
||||
"css",
|
||||
"scss",
|
||||
"sass",
|
||||
"svg",
|
||||
]);
|
||||
|
||||
async function main() {
|
||||
const options = parseArgs(process.argv.slice(2));
|
||||
const config = await readClawhubConfig();
|
||||
const registry = (
|
||||
process.env.CLAWHUB_REGISTRY ||
|
||||
process.env.CLAWDHUB_REGISTRY ||
|
||||
config.registry ||
|
||||
DEFAULT_REGISTRY
|
||||
).replace(/\/+$/, "");
|
||||
|
||||
if (!config.token) {
|
||||
throw new Error("Not logged in. Run: clawhub login");
|
||||
}
|
||||
|
||||
await apiJson(registry, config.token, "/api/v1/whoami");
|
||||
|
||||
const roots = options.roots.length > 0 ? options.roots : [path.resolve("skills")];
|
||||
const skills = await findSkills(roots);
|
||||
|
||||
if (skills.length === 0) {
|
||||
throw new Error("No skills found.");
|
||||
}
|
||||
|
||||
console.log("ClawHub sync");
|
||||
console.log(`Roots with skills: ${roots.join(", ")}`);
|
||||
|
||||
const locals = await mapWithConcurrency(skills, options.concurrency, async (skill) => {
|
||||
const files = await listTextFiles(skill.folder);
|
||||
const fingerprint = buildFingerprint(files);
|
||||
return {
|
||||
...skill,
|
||||
fileCount: files.length,
|
||||
fingerprint,
|
||||
};
|
||||
});
|
||||
|
||||
const candidates = await mapWithConcurrency(locals, options.concurrency, async (skill) => {
|
||||
const query = new URLSearchParams({
|
||||
slug: skill.slug,
|
||||
hash: skill.fingerprint,
|
||||
});
|
||||
const { status, body } = await apiJsonWithStatus(
|
||||
registry,
|
||||
config.token,
|
||||
`/api/v1/resolve?${query.toString()}`
|
||||
);
|
||||
|
||||
if (status === 404) {
|
||||
return {
|
||||
...skill,
|
||||
status: "new",
|
||||
latestVersion: null,
|
||||
matchVersion: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (status !== 200) {
|
||||
throw new Error(body?.message || `Resolve failed for ${skill.slug} (HTTP ${status})`);
|
||||
}
|
||||
|
||||
const latestVersion = body?.latestVersion?.version ?? null;
|
||||
const matchVersion = body?.match?.version ?? null;
|
||||
|
||||
if (!latestVersion) {
|
||||
return {
|
||||
...skill,
|
||||
status: "new",
|
||||
latestVersion: null,
|
||||
matchVersion: null,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...skill,
|
||||
status: matchVersion ? "synced" : "update",
|
||||
latestVersion,
|
||||
matchVersion,
|
||||
};
|
||||
});
|
||||
|
||||
const actionable = candidates.filter((candidate) => candidate.status !== "synced");
|
||||
if (actionable.length === 0) {
|
||||
console.log("Nothing to sync.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("");
|
||||
console.log("To sync");
|
||||
for (const candidate of actionable) {
|
||||
console.log(`- ${formatCandidate(candidate, options.bump)}`);
|
||||
}
|
||||
|
||||
if (options.dryRun) {
|
||||
console.log("");
|
||||
console.log(`Dry run: would upload ${actionable.length} skill(s).`);
|
||||
return;
|
||||
}
|
||||
|
||||
const tags = options.tags
|
||||
.split(",")
|
||||
.map((tag) => tag.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
for (const candidate of actionable) {
|
||||
const version =
|
||||
candidate.status === "new"
|
||||
? "1.0.0"
|
||||
: bumpSemver(candidate.latestVersion, options.bump);
|
||||
|
||||
console.log(`Publishing ${candidate.slug}@${version}`);
|
||||
const files = await listTextFiles(candidate.folder);
|
||||
await publishSkill({
|
||||
registry,
|
||||
token: config.token,
|
||||
skill: candidate,
|
||||
files,
|
||||
version,
|
||||
changelog: options.changelog,
|
||||
tags,
|
||||
});
|
||||
}
|
||||
|
||||
console.log("");
|
||||
console.log(`Uploaded ${actionable.length} skill(s).`);
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const options = {
|
||||
roots: [],
|
||||
dryRun: false,
|
||||
bump: "patch",
|
||||
changelog: "",
|
||||
tags: "latest",
|
||||
concurrency: 4,
|
||||
};
|
||||
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index];
|
||||
if (arg === "--dry-run") {
|
||||
options.dryRun = true;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--all") {
|
||||
continue;
|
||||
}
|
||||
if (arg === "--root") {
|
||||
const value = argv[index + 1];
|
||||
if (!value) throw new Error("--root requires a directory");
|
||||
options.roots.push(path.resolve(value));
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--bump") {
|
||||
const value = argv[index + 1];
|
||||
if (!["patch", "minor", "major"].includes(value)) {
|
||||
throw new Error("--bump must be patch, minor, or major");
|
||||
}
|
||||
options.bump = value;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--changelog") {
|
||||
const value = argv[index + 1];
|
||||
if (value == null) throw new Error("--changelog requires text");
|
||||
options.changelog = value;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--tags") {
|
||||
const value = argv[index + 1];
|
||||
if (value == null) throw new Error("--tags requires a value");
|
||||
options.tags = value;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--concurrency") {
|
||||
const value = Number(argv[index + 1]);
|
||||
if (!Number.isInteger(value) || value < 1 || value > 32) {
|
||||
throw new Error("--concurrency must be an integer between 1 and 32");
|
||||
}
|
||||
options.concurrency = value;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg === "-h" || arg === "--help") {
|
||||
printUsage();
|
||||
process.exit(0);
|
||||
}
|
||||
throw new Error(`Unknown argument: ${arg}`);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function printUsage() {
|
||||
console.log(`Usage: sync-clawhub.mjs [options]
|
||||
|
||||
Options:
|
||||
--root <dir> Extra skill root (repeatable)
|
||||
--all Accepted for compatibility
|
||||
--dry-run Show what would be uploaded
|
||||
--bump <type> patch | minor | major
|
||||
--changelog <text> Changelog for updates
|
||||
--tags <tags> Comma-separated tags
|
||||
--concurrency <n> Registry check concurrency (1-32)
|
||||
-h, --help Show help`);
|
||||
}
|
||||
|
||||
async function readClawhubConfig() {
|
||||
const configPath = getConfigPath();
|
||||
try {
|
||||
return JSON.parse(await fs.readFile(configPath, "utf8"));
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function getConfigPath() {
|
||||
const override =
|
||||
process.env.CLAWHUB_CONFIG_PATH?.trim() || process.env.CLAWDHUB_CONFIG_PATH?.trim();
|
||||
if (override) {
|
||||
return path.resolve(override);
|
||||
}
|
||||
|
||||
const home = os.homedir();
|
||||
if (process.platform === "darwin") {
|
||||
const clawhub = path.join(home, "Library", "Application Support", "clawhub", "config.json");
|
||||
const clawdhub = path.join(home, "Library", "Application Support", "clawdhub", "config.json");
|
||||
return pathForExistingConfig(clawhub, clawdhub);
|
||||
}
|
||||
|
||||
const xdg = process.env.XDG_CONFIG_HOME;
|
||||
if (xdg) {
|
||||
const clawhub = path.join(xdg, "clawhub", "config.json");
|
||||
const clawdhub = path.join(xdg, "clawdhub", "config.json");
|
||||
return pathForExistingConfig(clawhub, clawdhub);
|
||||
}
|
||||
|
||||
if (process.platform === "win32" && process.env.APPDATA) {
|
||||
const clawhub = path.join(process.env.APPDATA, "clawhub", "config.json");
|
||||
const clawdhub = path.join(process.env.APPDATA, "clawdhub", "config.json");
|
||||
return pathForExistingConfig(clawhub, clawdhub);
|
||||
}
|
||||
|
||||
const clawhub = path.join(home, ".config", "clawhub", "config.json");
|
||||
const clawdhub = path.join(home, ".config", "clawdhub", "config.json");
|
||||
return pathForExistingConfig(clawhub, clawdhub);
|
||||
}
|
||||
|
||||
function pathForExistingConfig(primary, legacy) {
|
||||
if (existsSync(primary)) return path.resolve(primary);
|
||||
if (existsSync(legacy)) return path.resolve(legacy);
|
||||
return path.resolve(primary);
|
||||
}
|
||||
|
||||
async function findSkills(roots) {
|
||||
const deduped = new Map();
|
||||
for (const root of roots) {
|
||||
const folders = await findSkillFolders(root);
|
||||
for (const folder of folders) {
|
||||
deduped.set(folder.slug, folder);
|
||||
}
|
||||
}
|
||||
return [...deduped.values()].sort((left, right) => left.slug.localeCompare(right.slug));
|
||||
}
|
||||
|
||||
async function findSkillFolders(root) {
|
||||
const stat = await safeStat(root);
|
||||
if (!stat?.isDirectory()) return [];
|
||||
|
||||
if (await hasSkillMarker(root)) {
|
||||
return [buildSkillEntry(root)];
|
||||
}
|
||||
|
||||
const entries = await fs.readdir(root, { withFileTypes: true });
|
||||
const found = [];
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
const folder = path.join(root, entry.name);
|
||||
if (await hasSkillMarker(folder)) {
|
||||
found.push(buildSkillEntry(folder));
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
function buildSkillEntry(folder) {
|
||||
const base = path.basename(folder);
|
||||
return {
|
||||
folder,
|
||||
slug: sanitizeSlug(base),
|
||||
displayName: titleCase(base),
|
||||
};
|
||||
}
|
||||
|
||||
async function hasSkillMarker(folder) {
|
||||
return Boolean(
|
||||
(await safeStat(path.join(folder, "SKILL.md")))?.isFile() ||
|
||||
(await safeStat(path.join(folder, "skill.md")))?.isFile()
|
||||
);
|
||||
}
|
||||
|
||||
async function listTextFiles(root) {
|
||||
const files = [];
|
||||
|
||||
async function walk(folder) {
|
||||
const entries = await fs.readdir(folder, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.name.startsWith(".")) continue;
|
||||
if (entry.name === "node_modules") continue;
|
||||
if (entry.name === ".clawhub" || entry.name === ".clawdhub") continue;
|
||||
|
||||
const fullPath = path.join(folder, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
await walk(fullPath);
|
||||
continue;
|
||||
}
|
||||
if (!entry.isFile()) continue;
|
||||
|
||||
const relPath = path.relative(root, fullPath).split(path.sep).join("/");
|
||||
const ext = relPath.split(".").pop()?.toLowerCase() ?? "";
|
||||
if (!TEXT_EXTENSIONS.has(ext)) continue;
|
||||
|
||||
const bytes = await fs.readFile(fullPath);
|
||||
files.push({ relPath, bytes });
|
||||
}
|
||||
}
|
||||
|
||||
await walk(root);
|
||||
files.sort((left, right) => left.relPath.localeCompare(right.relPath));
|
||||
return files;
|
||||
}
|
||||
|
||||
function buildFingerprint(files) {
|
||||
const payload = files
|
||||
.map((file) => `${file.relPath}:${sha256(file.bytes)}`)
|
||||
.sort((left, right) => left.localeCompare(right))
|
||||
.join("\n");
|
||||
return crypto.createHash("sha256").update(payload).digest("hex");
|
||||
}
|
||||
|
||||
function sha256(bytes) {
|
||||
return crypto.createHash("sha256").update(bytes).digest("hex");
|
||||
}
|
||||
|
||||
async function publishSkill({ registry, token, skill, files, version, changelog, tags }) {
|
||||
const form = new FormData();
|
||||
form.set(
|
||||
"payload",
|
||||
JSON.stringify({
|
||||
slug: skill.slug,
|
||||
displayName: skill.displayName,
|
||||
version,
|
||||
changelog,
|
||||
tags,
|
||||
acceptLicenseTerms: true,
|
||||
})
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
form.append("files", new Blob([file.bytes], { type: "text/plain" }), file.relPath);
|
||||
}
|
||||
|
||||
const response = await fetch(`${registry}/api/v1/skills`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: form,
|
||||
});
|
||||
|
||||
const text = await response.text();
|
||||
if (!response.ok) {
|
||||
throw new Error(text || `Publish failed for ${skill.slug} (HTTP ${response.status})`);
|
||||
}
|
||||
|
||||
const result = text ? JSON.parse(text) : {};
|
||||
console.log(`OK. Published ${skill.slug}@${version}${result.versionId ? ` (${result.versionId})` : ""}`);
|
||||
}
|
||||
|
||||
async function apiJson(registry, token, requestPath) {
|
||||
const { status, body } = await apiJsonWithStatus(registry, token, requestPath);
|
||||
if (status < 200 || status >= 300) {
|
||||
throw new Error(body?.message || `HTTP ${status}`);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
async function apiJsonWithStatus(registry, token, requestPath) {
|
||||
const response = await fetch(`${registry}${requestPath}`, {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
const text = await response.text();
|
||||
let body = null;
|
||||
try {
|
||||
body = text ? JSON.parse(text) : null;
|
||||
} catch {
|
||||
body = { message: text };
|
||||
}
|
||||
return { status: response.status, body };
|
||||
}
|
||||
|
||||
async function mapWithConcurrency(items, limit, fn) {
|
||||
const results = new Array(items.length);
|
||||
let cursor = 0;
|
||||
|
||||
async function worker() {
|
||||
while (cursor < items.length) {
|
||||
const index = cursor;
|
||||
cursor += 1;
|
||||
results[index] = await fn(items[index], index);
|
||||
}
|
||||
}
|
||||
|
||||
const count = Math.min(Math.max(limit, 1), Math.max(items.length, 1));
|
||||
await Promise.all(Array.from({ length: count }, () => worker()));
|
||||
return results;
|
||||
}
|
||||
|
||||
function formatCandidate(candidate, bump) {
|
||||
if (candidate.status === "new") {
|
||||
return `${candidate.slug} NEW (${candidate.fileCount} files)`;
|
||||
}
|
||||
return `${candidate.slug} UPDATE ${candidate.latestVersion} -> ${bumpSemver(
|
||||
candidate.latestVersion,
|
||||
bump
|
||||
)} (${candidate.fileCount} files)`;
|
||||
}
|
||||
|
||||
function bumpSemver(version, bump) {
|
||||
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version ?? "");
|
||||
if (!match) {
|
||||
throw new Error(`Invalid semver: ${version}`);
|
||||
}
|
||||
const major = Number(match[1]);
|
||||
const minor = Number(match[2]);
|
||||
const patch = Number(match[3]);
|
||||
|
||||
if (bump === "major") return `${major + 1}.0.0`;
|
||||
if (bump === "minor") return `${major}.${minor + 1}.0`;
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
}
|
||||
|
||||
function sanitizeSlug(value) {
|
||||
return value
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9-]+/g, "-")
|
||||
.replace(/^-+/, "")
|
||||
.replace(/-+$/, "")
|
||||
.replace(/--+/g, "-");
|
||||
}
|
||||
|
||||
function titleCase(value) {
|
||||
return value
|
||||
.trim()
|
||||
.replace(/[-_]+/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.replace(/\b\w/g, (char) => char.toUpperCase());
|
||||
}
|
||||
|
||||
async function safeStat(filePath) {
|
||||
try {
|
||||
return await fs.stat(filePath);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
});
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SKILLS_DIR="${ROOT_DIR}/skills"
|
||||
|
||||
if ! command -v node >/dev/null 2>&1; then
|
||||
echo "Error: node is required."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
set -- --all
|
||||
fi
|
||||
|
||||
exec node "${ROOT_DIR}/scripts/sync-clawhub.mjs" --root "${SKILLS_DIR}" "$@"
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
SRC="$REPO_ROOT/skills/baoyu-markdown-to-html/scripts/md/"
|
||||
DEST="$REPO_ROOT/skills/baoyu-post-to-wechat/scripts/md/"
|
||||
|
||||
echo "Syncing: $SRC → $DEST"
|
||||
|
||||
rsync -av --delete \
|
||||
--exclude 'node_modules/' \
|
||||
--exclude 'package-lock.json' \
|
||||
"$SRC" "$DEST"
|
||||
|
||||
echo "Installing dependencies..."
|
||||
cd "$DEST" && npm install
|
||||
|
||||
echo "Done."
|
||||
@@ -1,452 +1,174 @@
|
||||
---
|
||||
name: baoyu-article-illustrator
|
||||
description: Analyzes article structure, identifies positions requiring visual aids, generates illustrations with Type × Style two-dimension approach. Use when user asks to "illustrate article", "add images", "generate images for article", or "为文章配图".
|
||||
version: 1.56.1
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-article-illustrator
|
||||
---
|
||||
|
||||
# Article Illustrator
|
||||
|
||||
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 | infographic, scene, flowchart, comparison, framework, timeline |
|
||||
| **Style** | Visual aesthetics | notion, warm, minimal, blueprint, watercolor, elegant |
|
||||
|
||||
Type × Style can be freely combined. Example: `--type infographic --style blueprint` creates technical data visualization with schematic aesthetics.
|
||||
Combine freely: `--type infographic --style blueprint`
|
||||
|
||||
## Type Gallery
|
||||
Or use presets: `--preset tech-explainer` → type + style in one flag. See [Style Presets](references/style-presets.md).
|
||||
|
||||
| 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 |
|
||||
## Types
|
||||
|
||||
## Density Options
|
||||
| Type | Best For |
|
||||
|------|----------|
|
||||
| `infographic` | Data, metrics, technical |
|
||||
| `scene` | Narratives, emotional |
|
||||
| `flowchart` | Processes, workflows |
|
||||
| `comparison` | Side-by-side, options |
|
||||
| `framework` | Models, architecture |
|
||||
| `timeline` | History, evolution |
|
||||
|
||||
| Density | Count | Description |
|
||||
|---------|-------|-------------|
|
||||
| `minimal` | 1-2 | Core concepts only |
|
||||
| `balanced` (Default) | 3-5 | Major sections coverage |
|
||||
| `rich` | 6+ | Rich visual support |
|
||||
## Styles
|
||||
|
||||
## Style Gallery
|
||||
See [references/styles.md](references/styles.md) for Core Styles, full gallery, and Type × Style compatibility.
|
||||
|
||||
| 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 |
|
||||
## Workflow
|
||||
|
||||
Full style specifications: `references/styles/<style>.md`
|
||||
```
|
||||
- [ ] Step 1: Pre-check (EXTEND.md, references, config)
|
||||
- [ ] Step 2: Analyze content
|
||||
- [ ] Step 3: Confirm settings (AskUserQuestion)
|
||||
- [ ] Step 4: Generate outline
|
||||
- [ ] Step 5: Generate images
|
||||
- [ ] Step 6: Finalize
|
||||
```
|
||||
|
||||
## Type × Style Compatibility
|
||||
### Step 1: Pre-check
|
||||
|
||||
| | notion | warm | minimal | blueprint | watercolor | elegant | editorial | scientific |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| infographic | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
|
||||
| scene | ✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ | ✗ |
|
||||
| flowchart | ✓✓ | ✓ | ✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ |
|
||||
| comparison | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ |
|
||||
| framework | ✓✓ | ✓ | ✓✓ | ✓✓ | ✗ | ✓✓ | ✓ | ✓✓ |
|
||||
| timeline | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ |
|
||||
**1.5 Load Preferences (EXTEND.md) ⛔ BLOCKING**
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
```bash
|
||||
# macOS, Linux, WSL, Git Bash
|
||||
test -f .baoyu-skills/baoyu-article-illustrator/EXTEND.md && echo "project"
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "xdg"
|
||||
test -f "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "user"
|
||||
```
|
||||
|
||||
## Auto Selection
|
||||
```powershell
|
||||
# PowerShell (Windows)
|
||||
if (Test-Path .baoyu-skills/baoyu-article-illustrator/EXTEND.md) { "project" }
|
||||
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
|
||||
if (Test-Path "$xdg/baoyu-skills/baoyu-article-illustrator/EXTEND.md") { "xdg" }
|
||||
if (Test-Path "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md") { "user" }
|
||||
```
|
||||
|
||||
| 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 |
|
||||
| Result | Action |
|
||||
|--------|--------|
|
||||
| Found | Read, parse, display summary |
|
||||
| Not found | ⛔ Run [first-time-setup](references/config/first-time-setup.md) |
|
||||
|
||||
Full procedures: [references/workflow.md](references/workflow.md#step-1-pre-check)
|
||||
|
||||
### Step 2: Analyze
|
||||
|
||||
| Analysis | Output |
|
||||
|----------|--------|
|
||||
| Content type | Technical / Tutorial / Methodology / Narrative |
|
||||
| Purpose | information / visualization / imagination |
|
||||
| Core arguments | 2-5 main points |
|
||||
| Positions | Where illustrations add value |
|
||||
|
||||
**CRITICAL**: Metaphors → visualize underlying concept, NOT literal image.
|
||||
|
||||
Full procedures: [references/workflow.md](references/workflow.md#step-2-setup--analyze)
|
||||
|
||||
### Step 3: Confirm Settings ⚠️
|
||||
|
||||
**ONE AskUserQuestion, max 4 Qs. Q1-Q2 REQUIRED. Q3 required unless preset chosen.**
|
||||
|
||||
| Q | Options |
|
||||
|---|---------|
|
||||
| **Q1: Preset or Type** | [Recommended preset], [alt preset], or manual: infographic, scene, flowchart, comparison, framework, timeline, mixed |
|
||||
| **Q2: Density** | minimal (1-2), balanced (3-5), per-section (Recommended), rich (6+) |
|
||||
| **Q3: Style** | [Recommended], minimal-flat, sci-fi, hand-drawn, editorial, scene, poster, Other — **skip if preset chosen** |
|
||||
| Q4: Language | When article language ≠ EXTEND.md setting |
|
||||
|
||||
Full procedures: [references/workflow.md](references/workflow.md#step-3-confirm-settings-)
|
||||
|
||||
### Step 4: Generate Outline
|
||||
|
||||
Save `outline.md` with frontmatter (type, density, style, image_count) and entries:
|
||||
|
||||
```yaml
|
||||
## Illustration 1
|
||||
**Position**: [section/paragraph]
|
||||
**Purpose**: [why]
|
||||
**Visual Content**: [what]
|
||||
**Filename**: 01-infographic-concept-name.png
|
||||
```
|
||||
|
||||
Full template: [references/workflow.md](references/workflow.md#step-4-generate-outline)
|
||||
|
||||
### Step 5: Generate Images
|
||||
|
||||
⛔ **BLOCKING: Prompt files MUST be saved before ANY image generation.**
|
||||
|
||||
**Execution strategy**: When multiple illustrations have saved prompt files and the task is now plain generation, prefer `baoyu-image-gen` batch mode (`build-batch.ts` → `--batchfile`) over spawning subagents. Use subagents only when each image still needs separate prompt iteration or creative exploration.
|
||||
|
||||
1. For each illustration, create a prompt file per [references/prompt-construction.md](references/prompt-construction.md)
|
||||
2. Save to `prompts/NN-{type}-{slug}.md` with YAML frontmatter
|
||||
3. Prompts **MUST** use type-specific templates with structured sections (ZONES / LABELS / COLORS / STYLE / ASPECT)
|
||||
4. LABELS **MUST** include article-specific data: actual numbers, terms, metrics, quotes
|
||||
5. **DO NOT** pass ad-hoc inline prompts to `--prompt` without saving prompt files first
|
||||
6. Select generation skill, process references (`direct`/`style`/`palette`)
|
||||
7. Apply watermark if EXTEND.md enabled
|
||||
8. Generate from saved prompt files; retry once on failure
|
||||
|
||||
Full procedures: [references/workflow.md](references/workflow.md#step-5-generate-images)
|
||||
|
||||
### Step 6: Finalize
|
||||
|
||||
Insert `` after paragraphs.
|
||||
|
||||
```
|
||||
Article Illustration Complete!
|
||||
Article: [path] | Type: [type] | Density: [level] | Style: [style]
|
||||
Images: X/N generated
|
||||
```
|
||||
|
||||
## Output Directory
|
||||
|
||||
```
|
||||
illustrations/{topic-slug}/
|
||||
├── source-{slug}.{ext}
|
||||
├── references/ # if provided
|
||||
├── 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.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Progress
|
||||
|
||||
```
|
||||
- [ ] Step 1: Setup & Analyze
|
||||
- [ ] Step 2: Confirm Settings ⚠️ REQUIRED
|
||||
- [ ] Step 3: Generate Outline
|
||||
- [ ] Step 4: Generate Images
|
||||
- [ ] Step 5: Finalize
|
||||
```
|
||||
|
||||
### Step 1: Setup & Analyze
|
||||
|
||||
**1.1 Load Preferences (EXTEND.md)**
|
||||
|
||||
Use Bash to check EXTEND.md existence (priority order):
|
||||
|
||||
```bash
|
||||
# Check project-level first
|
||||
test -f .baoyu-skills/baoyu-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 user with AskUserQuestion (see references/config/first-time-setup.md) │
|
||||
└───────────┴───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
**EXTEND.md Supports**: Watermark | Preferred type/style | Custom style definitions | Language preference
|
||||
|
||||
Schema: `references/config/preferences-schema.md`
|
||||
|
||||
**1.2 Analyze Content**
|
||||
|
||||
Read article, detect language, classify content.
|
||||
|
||||
| Analysis | Description |
|
||||
|----------|-------------|
|
||||
| Content type | Technical / Tutorial / Methodology / Narrative |
|
||||
| Core arguments | 2-5 main points that MUST be visualized |
|
||||
| Visual opportunities | Positions where illustrations add value |
|
||||
| Recommended type | Based on content signals |
|
||||
| Recommended density | Based on article length and complexity |
|
||||
|
||||
**1.3 Extract Core Arguments**
|
||||
|
||||
Extract 2-5 core arguments that MUST be visualized:
|
||||
- Main thesis
|
||||
- Key concepts reader needs
|
||||
- Comparisons/contrasts being made
|
||||
- Framework/model proposed
|
||||
|
||||
**CRITICAL**: If article uses metaphors (e.g., "电锯切西瓜"), do NOT illustrate literally. Visualize the **underlying concept** instead.
|
||||
|
||||
**1.4 Identify Positions**
|
||||
|
||||
**What to Illustrate**:
|
||||
- Core arguments (REQUIRED)
|
||||
- Abstract concepts needing visualization
|
||||
- Data comparisons, metrics
|
||||
- Processes, workflows
|
||||
|
||||
**What NOT to Illustrate**:
|
||||
- Metaphors literally
|
||||
- Decorative scenes without information
|
||||
- Generic illustrations
|
||||
|
||||
### Step 2: Confirm Settings ⚠️
|
||||
|
||||
**Do NOT skip.** Use AskUserQuestion with 3-4 questions in ONE call.
|
||||
|
||||
**Question 1: Illustration Type**
|
||||
|
||||
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
|
||||
|
||||
**Question 2: Density**
|
||||
|
||||
- minimal (1-2 images) - Core concepts only
|
||||
- balanced (3-5 images) (Recommended) - Major sections
|
||||
- rich (6+ images) - Comprehensive visual support
|
||||
|
||||
**Question 3: 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]
|
||||
|
||||
**Question 4** (only if source ≠ user language):
|
||||
- Language: Source language / User language
|
||||
|
||||
### Step 3: Generate Outline
|
||||
|
||||
Based on confirmed Type + Density + Style, generate illustration outline.
|
||||
|
||||
**Outline Format** (`outline.md`):
|
||||
|
||||
```yaml
|
||||
---
|
||||
type: infographic
|
||||
density: balanced
|
||||
style: blueprint
|
||||
image_count: 4
|
||||
---
|
||||
|
||||
## Illustration 1
|
||||
|
||||
**Position**: [section] / [paragraph]
|
||||
**Purpose**: [why this illustration helps]
|
||||
**Visual Content**: [what to show]
|
||||
**Type Application**: [how type applies here]
|
||||
**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
|
||||
|
||||
### Step 4: Generate Images
|
||||
|
||||
**4.1 Create Prompts**
|
||||
|
||||
Follow Prompt Construction principles below. Save each to `prompts/illustration-{slug}.md`.
|
||||
|
||||
**4.2 Select Generation Skill**
|
||||
|
||||
Check available image generation skills. If multiple, ask user to choose.
|
||||
|
||||
**4.3 Apply Watermark** (if enabled in preferences)
|
||||
|
||||
Add to prompt: `Include a subtle watermark "[content]" positioned at [position] with approximately [opacity*100]% visibility.`
|
||||
|
||||
**4.4 Generate**
|
||||
|
||||
1. Generate sequentially
|
||||
2. After each: "Generated X/N"
|
||||
3. On failure: auto-retry once, then log and continue
|
||||
|
||||
### Step 5: Finalize
|
||||
|
||||
**5.1 Update Article**
|
||||
|
||||
Insert after corresponding paragraph:
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
Alt text: concise description in article's language.
|
||||
|
||||
**5.2 Output Summary**
|
||||
|
||||
```
|
||||
Article Illustration Complete!
|
||||
|
||||
Article: [path]
|
||||
Type: [type name]
|
||||
Density: [minimal/balanced/rich]
|
||||
Style: [style name]
|
||||
Location: [directory path]
|
||||
Images: X/N generated
|
||||
|
||||
Positions:
|
||||
- 01-infographic-xxx.png → After "[Section]"
|
||||
- 02-infographic-yyy.png → After "[Section]"
|
||||
|
||||
[If failures]
|
||||
Failed:
|
||||
- NN-type-zzz.png: [reason]
|
||||
```
|
||||
|
||||
## 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 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
|
||||
```
|
||||
|
||||
**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 words, kebab-case. **Conflict**: append `-YYYYMMDD-HHMMSS`.
|
||||
|
||||
## Modification
|
||||
|
||||
| Action | Steps |
|
||||
|--------|-------|
|
||||
| **Edit** | Update prompt → Regenerate → Update reference |
|
||||
| **Add** | Identify position → Create prompt → Generate → Update outline → Insert reference |
|
||||
| **Delete** | Delete files → Remove reference → Update outline |
|
||||
| Edit | Update prompt → Regenerate → Update reference |
|
||||
| Add | Position → 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/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.
|
||||
| [references/workflow.md](references/workflow.md) | Detailed procedures |
|
||||
| [references/usage.md](references/usage.md) | Command syntax |
|
||||
| [references/styles.md](references/styles.md) | Style gallery |
|
||||
| [references/style-presets.md](references/style-presets.md) | Preset shortcuts (type + style) |
|
||||
| [references/prompt-construction.md](references/prompt-construction.md) | Prompt templates |
|
||||
| [references/config/first-time-setup.md](references/config/first-time-setup.md) | First-time setup |
|
||||
|
||||
@@ -9,6 +9,14 @@ description: First-time setup flow for baoyu-article-illustrator preferences
|
||||
|
||||
When no EXTEND.md is found, guide user through preference setup.
|
||||
|
||||
**⛔ BLOCKING OPERATION**: This setup MUST complete before ANY other workflow steps. Do NOT:
|
||||
- Ask about reference images
|
||||
- Ask about content/article
|
||||
- Ask about type or style preferences
|
||||
- Proceed to content analysis
|
||||
|
||||
ONLY ask the questions in this setup flow, save EXTEND.md, then continue.
|
||||
|
||||
## Setup Flow
|
||||
|
||||
```
|
||||
|
||||
@@ -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,284 @@
|
||||
# Prompt Construction
|
||||
|
||||
## Prompt File Format
|
||||
|
||||
Each prompt file uses YAML frontmatter + content:
|
||||
|
||||
```yaml
|
||||
---
|
||||
illustration_id: 01
|
||||
type: infographic
|
||||
style: blueprint
|
||||
references: # ⚠️ ONLY if files EXIST in references/ directory
|
||||
- ref_id: 01
|
||||
filename: 01-ref-diagram.png
|
||||
usage: direct # direct | style | palette
|
||||
---
|
||||
|
||||
[Type-specific template content below...]
|
||||
```
|
||||
|
||||
**⚠️ CRITICAL - When to include `references` field**:
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| Reference file saved to `references/` | Include in frontmatter ✓ |
|
||||
| Style extracted verbally (no file) | DO NOT include in frontmatter, append to prompt body instead |
|
||||
| File path in frontmatter but file doesn't exist | ERROR - remove references field |
|
||||
|
||||
**Reference Usage Types** (only when file exists):
|
||||
|
||||
| Usage | Description | Generation Action |
|
||||
|-------|-------------|-------------------|
|
||||
| `direct` | Primary visual reference | Pass to `--ref` parameter |
|
||||
| `style` | Style characteristics only | Describe style in prompt text |
|
||||
| `palette` | Color palette extraction | Include colors in prompt |
|
||||
|
||||
**If no reference file but style/palette extracted verbally**, append directly to prompt body:
|
||||
```
|
||||
COLORS (from reference):
|
||||
- Primary: #E8756D coral
|
||||
- Secondary: #7ECFC0 mint
|
||||
...
|
||||
|
||||
STYLE (from reference):
|
||||
- Clean lines, minimal shadows
|
||||
- Gradient backgrounds
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Default Composition Requirements
|
||||
|
||||
**Apply to ALL prompts by default**:
|
||||
|
||||
| Requirement | Description |
|
||||
|-------------|-------------|
|
||||
| **Clean composition** | Simple layouts, no visual clutter |
|
||||
| **White space** | Generous margins, breathing room around elements |
|
||||
| **No complex backgrounds** | Solid colors or subtle gradients only, avoid busy textures |
|
||||
| **Centered or content-appropriate** | Main visual elements centered or positioned by content needs |
|
||||
| **Matching graphics** | Use graphic elements that align with content theme |
|
||||
| **Highlight core info** | White space draws attention to key information |
|
||||
|
||||
**Add to ALL prompts**:
|
||||
> Clean composition with generous white space. Simple or no background. Main elements centered or positioned by content needs.
|
||||
|
||||
---
|
||||
|
||||
## Character Rendering
|
||||
|
||||
When depicting people:
|
||||
|
||||
| Guideline | Description |
|
||||
|-----------|-------------|
|
||||
| **Style** | Simplified cartoon silhouettes or symbolic expressions |
|
||||
| **Avoid** | Realistic human portrayals, detailed faces |
|
||||
| **Diversity** | Varied body types when showing multiple people |
|
||||
| **Emotion** | Express through posture and simple gestures |
|
||||
|
||||
**Add to ALL prompts with human figures**:
|
||||
> Human figures: simplified stylized silhouettes or symbolic representations, not photorealistic.
|
||||
|
||||
---
|
||||
|
||||
## Text in Illustrations
|
||||
|
||||
| Element | Guideline |
|
||||
|---------|-----------|
|
||||
| **Size** | Large, prominent, immediately readable |
|
||||
| **Style** | Handwritten fonts preferred for warmth |
|
||||
| **Content** | Concise keywords and core concepts only |
|
||||
| **Language** | Match article language |
|
||||
|
||||
**Add to prompts with text**:
|
||||
> Text should be large and prominent with handwritten-style fonts. Keep minimal, focus on keywords.
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
**Infographic + vector-illustration**:
|
||||
```
|
||||
Flat vector illustration infographic. Clean black outlines on all elements.
|
||||
COLORS: Cream background (#F5F0E6), Coral Red (#E07A5F), Mint Green (#81B29A), Mustard Yellow (#F2CC8F)
|
||||
ELEMENTS: Geometric simplified icons, no gradients, playful decorative elements (dots, stars)
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
**Flowchart + vector-illustration**:
|
||||
```
|
||||
Flat vector flowchart with bold arrows and geometric step containers.
|
||||
COLORS: Cream background (#F5F0E6), steps in Coral/Mint/Mustard, black outlines
|
||||
ELEMENTS: Rounded rectangles, thick arrows, simple icons per step
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
**Comparison + vector-illustration**:
|
||||
```
|
||||
Flat vector comparison with split layout. Clear visual separation.
|
||||
COLORS: Left side Coral (#E07A5F), Right side Mint (#81B29A), cream background
|
||||
ELEMENTS: Bold icons, black outlines, centered divider line
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
**Framework + vector-illustration**:
|
||||
```
|
||||
Flat vector framework diagram with geometric nodes and bold connectors.
|
||||
COLORS: Cream background (#F5F0E6), nodes in Coral/Mint/Mustard/Blue, black outlines
|
||||
ELEMENTS: Rounded rectangles or circles for nodes, thick connecting lines
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### Screen-Print Style Override
|
||||
|
||||
When `style: screen-print`, replace standard style instructions with:
|
||||
|
||||
```
|
||||
Screen print / silkscreen poster art. Flat color blocks, NO gradients.
|
||||
COLORS: 2-5 colors maximum. [Choose from style palette or duotone pair]
|
||||
TEXTURE: Halftone dot patterns, slight color layer misregistration, paper grain
|
||||
COMPOSITION: Bold silhouettes, geometric framing, negative space as storytelling element
|
||||
FIGURES: Silhouettes only, no detailed faces, stencil-cut edges
|
||||
TYPOGRAPHY: Bold condensed sans-serif integrated into composition (not overlaid)
|
||||
```
|
||||
|
||||
**Scene + screen-print**:
|
||||
```
|
||||
Conceptual poster scene. Single symbolic focal point, NOT literal illustration.
|
||||
COLORS: Duotone pair (e.g., Burnt Orange #E8751A + Deep Teal #0A6E6E) on Off-Black #121212
|
||||
COMPOSITION: Centered silhouette or geometric frame, 60%+ negative space
|
||||
TEXTURE: Halftone dots, paper grain, slight print misregistration
|
||||
```
|
||||
|
||||
**Comparison + screen-print**:
|
||||
```
|
||||
Split poster composition. Each side dominated by one color from duotone pair.
|
||||
LEFT: [Color A] side with silhouette/icon for [Option A]
|
||||
RIGHT: [Color B] side with silhouette/icon for [Option B]
|
||||
DIVIDER: Geometric shape or negative space boundary
|
||||
TEXTURE: Halftone transitions between sides
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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,72 @@
|
||||
# Style Presets
|
||||
|
||||
`--preset X` expands to a type + style combination. Users can override either dimension.
|
||||
|
||||
## By Category
|
||||
|
||||
### Technical & Engineering
|
||||
|
||||
| --preset | Type | Style | Best For |
|
||||
|----------|------|-------|----------|
|
||||
| `tech-explainer` | `infographic` | `blueprint` | API docs, system metrics, technical deep-dives |
|
||||
| `system-design` | `framework` | `blueprint` | Architecture diagrams, system design |
|
||||
| `architecture` | `framework` | `vector-illustration` | Component relationships, module structure |
|
||||
| `science-paper` | `infographic` | `scientific` | Research findings, lab results, academic |
|
||||
|
||||
### Knowledge & Education
|
||||
|
||||
| --preset | Type | Style | Best For |
|
||||
|----------|------|-------|----------|
|
||||
| `knowledge-base` | `infographic` | `vector-illustration` | Concept explainers, tutorials, how-to |
|
||||
| `saas-guide` | `infographic` | `notion` | Product guides, SaaS docs, tool walkthroughs |
|
||||
| `tutorial` | `flowchart` | `vector-illustration` | Step-by-step tutorials, setup guides |
|
||||
| `process-flow` | `flowchart` | `notion` | Workflow documentation, onboarding flows |
|
||||
|
||||
### Data & Analysis
|
||||
|
||||
| --preset | Type | Style | Best For |
|
||||
|----------|------|-------|----------|
|
||||
| `data-report` | `infographic` | `editorial` | Data journalism, metrics reports, dashboards |
|
||||
| `versus` | `comparison` | `vector-illustration` | Tech comparisons, framework shootouts |
|
||||
| `business-compare` | `comparison` | `elegant` | Product evaluations, strategy options |
|
||||
|
||||
### Narrative & Creative
|
||||
|
||||
| --preset | Type | Style | Best For |
|
||||
|----------|------|-------|----------|
|
||||
| `storytelling` | `scene` | `warm` | Personal essays, reflections, growth stories |
|
||||
| `lifestyle` | `scene` | `watercolor` | Travel, wellness, lifestyle, creative |
|
||||
| `history` | `timeline` | `elegant` | Historical overviews, milestones |
|
||||
| `evolution` | `timeline` | `warm` | Progress narratives, growth journeys |
|
||||
|
||||
### Editorial & Opinion
|
||||
|
||||
| --preset | Type | Style | Best For |
|
||||
|----------|------|-------|----------|
|
||||
| `opinion-piece` | `scene` | `screen-print` | Op-eds, commentary, critical essays |
|
||||
| `editorial-poster` | `comparison` | `screen-print` | Debate, contrasting viewpoints |
|
||||
| `cinematic` | `scene` | `screen-print` | Dramatic narratives, cultural essays |
|
||||
|
||||
## Content Type → Preset Recommendations
|
||||
|
||||
Use this table during Step 3 to recommend presets based on Step 2 content analysis:
|
||||
|
||||
| Content Type (Step 2) | Primary Preset | Alternatives |
|
||||
|------------------------|----------------|--------------|
|
||||
| Technical | `tech-explainer` | `system-design`, `architecture` |
|
||||
| Tutorial | `tutorial` | `process-flow`, `knowledge-base` |
|
||||
| Methodology / Framework | `system-design` | `architecture`, `process-flow` |
|
||||
| Data / Metrics | `data-report` | `versus`, `tech-explainer` |
|
||||
| Comparison / Review | `versus` | `business-compare`, `editorial-poster` |
|
||||
| Narrative / Personal | `storytelling` | `lifestyle`, `evolution` |
|
||||
| Opinion / Editorial | `opinion-piece` | `cinematic`, `editorial-poster` |
|
||||
| Historical / Timeline | `history` | `evolution` |
|
||||
| Academic / Research | `science-paper` | `tech-explainer`, `data-report` |
|
||||
| SaaS / Product | `saas-guide` | `knowledge-base`, `process-flow` |
|
||||
|
||||
## Override Examples
|
||||
|
||||
- `--preset tech-explainer --style notion` = infographic type with notion style
|
||||
- `--preset storytelling --type timeline` = timeline type with warm style
|
||||
|
||||
Explicit `--type`/`--style` flags always override preset values.
|
||||
@@ -1,10 +1,29 @@
|
||||
# Style Reference
|
||||
|
||||
## Core Styles
|
||||
|
||||
Simplified style tier for quick selection:
|
||||
|
||||
| Core Style | Maps To | Best For |
|
||||
|------------|---------|----------|
|
||||
| `vector` | vector-illustration | Knowledge articles, tutorials, tech content |
|
||||
| `minimal-flat` | notion | General, knowledge sharing, SaaS |
|
||||
| `sci-fi` | blueprint | AI, frontier tech, system design |
|
||||
| `hand-drawn` | sketch/warm | Relaxed, reflective, casual content |
|
||||
| `editorial` | editorial | Processes, data, journalism |
|
||||
| `scene` | warm/watercolor | Narratives, emotional, lifestyle |
|
||||
| `poster` | screen-print | Opinion, editorial, cultural, cinematic |
|
||||
|
||||
Use Core Styles for most cases. See full Style Gallery below for granular control.
|
||||
|
||||
---
|
||||
|
||||
## Style Gallery
|
||||
|
||||
| Style | Description | Best For |
|
||||
|-------|-------------|----------|
|
||||
| `notion` (Default) | Minimalist hand-drawn line art | Knowledge sharing, SaaS, productivity |
|
||||
| `vector-illustration` | Clean flat vector art with bold shapes | Knowledge articles, tutorials, tech content |
|
||||
| `notion` | 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 |
|
||||
@@ -12,19 +31,32 @@
|
||||
| `watercolor` | Soft artistic with natural warmth | Lifestyle, travel, creative |
|
||||
| `editorial` | Magazine-style infographic | Tech explainers, journalism |
|
||||
| `scientific` | Academic precise diagrams | Biology, chemistry, technical research |
|
||||
| `chalkboard` | Classroom chalk drawing style | Education, teaching, explanations |
|
||||
| `fantasy-animation` | Ghibli/Disney-inspired hand-drawn | Storybook, magical, emotional |
|
||||
| `flat` | Modern bold geometric shapes | Modern digital, contemporary |
|
||||
| `flat-doodle` | Cute flat with bold outlines | Cute, friendly, approachable |
|
||||
| `intuition-machine` | Technical briefing with aged paper | Technical briefings, academic |
|
||||
| `nature` | Organic earthy illustration | Environmental, wellness |
|
||||
| `pixel-art` | Retro 8-bit gaming aesthetic | Gaming, retro tech |
|
||||
| `playful` | Whimsical pastel doodles | Fun, casual, educational |
|
||||
| `retro` | 80s/90s neon geometric | 80s/90s nostalgic, bold |
|
||||
| `sketch` | Raw pencil notebook style | Brainstorming, creative exploration |
|
||||
| `screen-print` | Bold poster art, halftone textures, limited colors | Opinion, editorial, cultural, cinematic |
|
||||
| `sketch-notes` | Soft hand-drawn warm notes | Educational, warm notes |
|
||||
| `vintage` | Aged parchment historical | Historical, heritage |
|
||||
|
||||
Full specifications: `references/styles/<style>.md`
|
||||
|
||||
## Type × Style Compatibility Matrix
|
||||
|
||||
| | notion | warm | minimal | blueprint | watercolor | elegant | editorial | scientific |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| infographic | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
|
||||
| scene | ✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ | ✗ |
|
||||
| flowchart | ✓✓ | ✓ | ✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ |
|
||||
| comparison | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ |
|
||||
| framework | ✓✓ | ✓ | ✓✓ | ✓✓ | ✗ | ✓✓ | ✓ | ✓✓ |
|
||||
| timeline | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ |
|
||||
| | vector-illustration | notion | warm | minimal | blueprint | watercolor | elegant | editorial | scientific | screen-print |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| infographic | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ |
|
||||
| scene | ✓ | ✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ | ✗ | ✓✓ |
|
||||
| flowchart | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✗ |
|
||||
| comparison | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ |
|
||||
| framework | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✗ | ✓✓ | ✓ | ✓✓ | ✓ |
|
||||
| timeline | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓ |
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
|
||||
@@ -32,30 +64,58 @@ Full specifications: `references/styles/<style>.md`
|
||||
|
||||
| Type | Primary Style | Secondary Styles |
|
||||
|------|---------------|------------------|
|
||||
| infographic | blueprint | notion, editorial, scientific |
|
||||
| infographic | vector-illustration | notion, blueprint, editorial |
|
||||
| scene | warm | watercolor, elegant |
|
||||
| flowchart | notion | blueprint, editorial |
|
||||
| comparison | notion | elegant, editorial |
|
||||
| framework | blueprint | notion, scientific |
|
||||
| flowchart | vector-illustration | notion, blueprint |
|
||||
| comparison | vector-illustration | notion, elegant |
|
||||
| framework | blueprint | vector-illustration, notion |
|
||||
| timeline | elegant | warm, editorial |
|
||||
|
||||
## Auto Selection by Content Signals
|
||||
|
||||
| Content Signals | Recommended Type | Recommended Style |
|
||||
|-----------------|------------------|-------------------|
|
||||
| API, metrics, data, comparison, numbers | infographic | blueprint, notion |
|
||||
| API, metrics, data, comparison, numbers | infographic | blueprint, vector-illustration |
|
||||
| Knowledge, concept, tutorial, learning, guide | infographic | vector-illustration, notion |
|
||||
| Tech, AI, programming, development, code | infographic | vector-illustration, blueprint |
|
||||
| How-to, steps, workflow, process, tutorial | flowchart | vector-illustration, notion |
|
||||
| Framework, model, architecture, principles | framework | blueprint, vector-illustration |
|
||||
| vs, pros/cons, before/after, alternatives | comparison | vector-illustration, 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 |
|
||||
| Knowledge, concept, productivity, SaaS, tool | infographic | notion |
|
||||
| Productivity, SaaS, tool, app, software | infographic | notion, vector-illustration |
|
||||
| Business, professional, strategy, corporate | framework | elegant |
|
||||
| Opinion, editorial, culture, philosophy, cinematic, dramatic, poster | scene | screen-print |
|
||||
| Biology, chemistry, medical, scientific | infographic | scientific |
|
||||
| Explainer, journalism, magazine, investigation | infographic | editorial |
|
||||
|
||||
## Style Characteristics by Type
|
||||
|
||||
### infographic + vector-illustration
|
||||
- Clean flat vector shapes, bold geometric forms
|
||||
- Vibrant but harmonious color palette
|
||||
- Clear visual hierarchy with icons and labels
|
||||
- Modern, professional, highly readable
|
||||
- Perfect for knowledge articles and tutorials
|
||||
|
||||
### flowchart + vector-illustration
|
||||
- Bold arrows and connectors
|
||||
- Distinct step containers with icons
|
||||
- Clean progression flow
|
||||
- High contrast for readability
|
||||
|
||||
### comparison + vector-illustration
|
||||
- Split layout with clear visual separation
|
||||
- Bold iconography for each side
|
||||
- Color-coded distinctions
|
||||
- Easy at-a-glance comparison
|
||||
|
||||
### framework + vector-illustration
|
||||
- Geometric node representations
|
||||
- Clear hierarchical structure
|
||||
- Bold connecting lines
|
||||
- Modern system diagram aesthetic
|
||||
|
||||
### infographic + blueprint
|
||||
- Technical precision, schematic lines
|
||||
- Grid-based layout, clear zones
|
||||
@@ -115,3 +175,22 @@ Full specifications: `references/styles/<style>.md`
|
||||
- Organic flow
|
||||
- Personal journey feel
|
||||
- Growth narratives
|
||||
|
||||
### scene + screen-print
|
||||
- Bold silhouettes, symbolic compositions
|
||||
- 2-5 flat colors with halftone textures
|
||||
- Figure-ground inversion (negative space tells secondary story)
|
||||
- Vintage poster aesthetic, conceptual not literal
|
||||
- Great for opinion pieces and cultural commentary
|
||||
|
||||
### comparison + screen-print
|
||||
- Split duotone composition (one color per side)
|
||||
- Bold geometric dividers
|
||||
- Symbolic icons over detailed rendering
|
||||
- High contrast, immediate visual impact
|
||||
|
||||
### framework + screen-print
|
||||
- Geometric node representations with stencil-cut edges
|
||||
- Limited color coding (one color per concept level)
|
||||
- Clean silhouette-based iconography
|
||||
- Poster-style hierarchy with bold typography
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# screen-print
|
||||
|
||||
Bold poster art with limited colors, halftone textures, and symbolic storytelling
|
||||
|
||||
## Design Aesthetic
|
||||
|
||||
Screen print / silkscreen aesthetic inspired by Mondo limited-edition posters and vintage concert prints. Flat color blocks, halftone dot patterns, bold silhouettes, and deliberate print imperfections. Conceptual and symbolic rather than literal — one iconic image tells the whole story. Perfect for opinion pieces, cultural commentary, and editorial content.
|
||||
|
||||
## Background
|
||||
|
||||
- Color: Off-Black (#121212) or Warm Cream (#F5E6D0)
|
||||
- Texture: Paper grain with subtle halftone dot overlay
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex | Usage |
|
||||
|------|-------|-----|-------|
|
||||
| Background | Off-Black | #121212 | Dark compositions |
|
||||
| Background Alt | Warm Cream | #F5E6D0 | Light compositions |
|
||||
| Primary | Burnt Orange | #E8751A | Main accent |
|
||||
| Secondary | Deep Teal | #0A6E6E | Contrast accent |
|
||||
| Tertiary | Crimson | #C0392B | Bold emphasis |
|
||||
| Highlight | Amber | #F4A623 | Small accents |
|
||||
| Text | Cream White | #FAF3E0 | On dark backgrounds |
|
||||
|
||||
**Duotone Pairs** (choose ONE pair for high-impact compositions):
|
||||
|
||||
| Pair | Color A | Color B | Feel |
|
||||
|------|---------|---------|------|
|
||||
| Orange + Teal | #E8751A | #0A6E6E | Cinematic, action |
|
||||
| Red + Cream | #C0392B | #F5E6D0 | Bold, classic |
|
||||
| Blue + Gold | #1A3A5C | #D4A843 | Prestigious, premium |
|
||||
| Crimson + Navy | #DC143C | #0D1B2A | Dramatic, noir |
|
||||
|
||||
**Rule**: Use 2-5 colors maximum. Fewer colors = stronger impact.
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Bold silhouettes and symbolic shapes
|
||||
- Halftone dot patterns within color fills
|
||||
- Slight color layer misregistration (print offset effect)
|
||||
- Geometric framing (circles, arches, triangles)
|
||||
- Figure-ground inversion (negative space forms secondary image)
|
||||
- Stencil-cut edges, no outlines — shapes defined by color boundaries
|
||||
- Typography integrated as design element, not overlay
|
||||
- Vintage poster border treatments
|
||||
|
||||
## Style Rules
|
||||
|
||||
### Do
|
||||
|
||||
- Limit to 2-5 flat colors
|
||||
- Use bold silhouettes over detailed rendering
|
||||
- Let negative space tell part of the story
|
||||
- Add halftone texture for authenticity
|
||||
- Use geometric composition (centered, symmetrical)
|
||||
- Reference vintage decades (60s/70s/80s) for era feel
|
||||
|
||||
### Don't
|
||||
|
||||
- Use photorealistic rendering or gradients
|
||||
- Add complex facial details (silhouettes preferred)
|
||||
- Mix too many visual elements (one focal point)
|
||||
- Use modern digital aesthetic
|
||||
- Create busy or cluttered compositions
|
||||
- Use more than 5 colors
|
||||
|
||||
## Best For
|
||||
|
||||
Opinion/editorial articles, cultural commentary, philosophy and strategy, dramatic narratives, cinematic storytelling, music and entertainment, event announcements, bold branding content
|
||||
@@ -0,0 +1,82 @@
|
||||
# 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) |
|
||||
| `--preset <name>` | Shorthand for type + style combo (see [references/style-presets.md](references/style-presets.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
|
||||
```
|
||||
|
||||
**Same thing with preset**:
|
||||
```bash
|
||||
/baoyu-article-illustrator api-design.md --preset tech-explainer
|
||||
```
|
||||
|
||||
**Personal story**:
|
||||
```bash
|
||||
/baoyu-article-illustrator journey.md --preset storytelling
|
||||
```
|
||||
|
||||
**Tutorial with steps**:
|
||||
```bash
|
||||
/baoyu-article-illustrator how-to-deploy.md --preset tutorial --density rich
|
||||
```
|
||||
|
||||
**Opinion article with poster style**:
|
||||
```bash
|
||||
/baoyu-article-illustrator opinion.md --preset opinion-piece
|
||||
```
|
||||
|
||||
**Preset with override**:
|
||||
```bash
|
||||
/baoyu-article-illustrator article.md --preset tech-explainer --style notion
|
||||
```
|
||||
@@ -0,0 +1,408 @@
|
||||
# Detailed Workflow Procedures
|
||||
|
||||
## Step 1: Pre-check
|
||||
|
||||
### 1.0 Detect & Save Reference Images ⚠️ REQUIRED if images provided
|
||||
|
||||
Check if user provided reference images. Handle based on input type:
|
||||
|
||||
| Input Type | Action |
|
||||
|------------|--------|
|
||||
| Image file path provided | Copy to `references/` subdirectory → can use `--ref` |
|
||||
| Image in conversation (no path) | **ASK user for file path** with AskUserQuestion |
|
||||
| User can't provide path | Extract style/palette verbally → append to prompts (NO frontmatter references) |
|
||||
|
||||
**CRITICAL**: Only add `references` to prompt frontmatter if files are ACTUALLY SAVED to `references/` directory.
|
||||
|
||||
**If user provides file path**:
|
||||
1. Copy to `references/NN-ref-{slug}.png`
|
||||
2. Create description: `references/NN-ref-{slug}.md`
|
||||
3. Verify files exist before proceeding
|
||||
|
||||
**If user can't provide path** (extracted verbally):
|
||||
1. Analyze image visually, extract: colors, style, composition
|
||||
2. Create `references/extracted-style.md` with extracted info
|
||||
3. DO NOT add `references` to prompt frontmatter
|
||||
4. Instead, append extracted style/colors directly to prompt text
|
||||
|
||||
**Description File Format** (only when file saved):
|
||||
```yaml
|
||||
---
|
||||
ref_id: NN
|
||||
filename: NN-ref-{slug}.png
|
||||
---
|
||||
[User's description or auto-generated description]
|
||||
```
|
||||
|
||||
**Verification** (only for saved files):
|
||||
```
|
||||
Reference Images Saved:
|
||||
- 01-ref-{slug}.png ✓ (can use --ref)
|
||||
- 02-ref-{slug}.png ✓ (can use --ref)
|
||||
```
|
||||
|
||||
**Or for extracted style**:
|
||||
```
|
||||
Reference Style Extracted (no file):
|
||||
- Colors: #E8756D coral, #7ECFC0 mint...
|
||||
- Style: minimal flat vector, clean lines...
|
||||
→ Will append to prompt text (not --ref)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.1 Determine Input Type
|
||||
|
||||
| Input | Output Directory | Next |
|
||||
|-------|------------------|------|
|
||||
| File path | Ask user (1.2) | → 1.2 |
|
||||
| Pasted content | `illustrations/{topic-slug}/` | → 1.4 |
|
||||
|
||||
**Backup rule for pasted content**: If `source.md` exists in target directory, rename to `source-backup-YYYYMMDD-HHMMSS.md` before saving.
|
||||
|
||||
### 1.2-1.4 Configuration (file path input only)
|
||||
|
||||
Check preferences and existing state, then ask ALL needed questions in ONE AskUserQuestion call (max 4 questions).
|
||||
|
||||
**Questions to include** (skip if preference exists or not applicable):
|
||||
|
||||
| Question | When to Ask | Options |
|
||||
|----------|-------------|---------|
|
||||
| Output directory | No `default_output_dir` in EXTEND.md | `{article-dir}/`, `{article-dir}/imgs/` (Recommended), `{article-dir}/illustrations/`, `illustrations/{topic-slug}/` |
|
||||
| Existing images | Target dir has `.png/.jpg/.webp` files | `supplement`, `overwrite`, `regenerate` |
|
||||
| Article update | Always (file path input) | `update`, `copy` |
|
||||
|
||||
**Preference Values** (if configured, skip asking):
|
||||
|
||||
| `default_output_dir` | Path |
|
||||
|----------------------|------|
|
||||
| `same-dir` | `{article-dir}/` |
|
||||
| `imgs-subdir` | `{article-dir}/imgs/` |
|
||||
| `illustrations-subdir` | `{article-dir}/illustrations/` |
|
||||
| `independent` | `illustrations/{topic-slug}/` |
|
||||
|
||||
### 1.5 Load Preferences (EXTEND.md) ⛔ BLOCKING
|
||||
|
||||
**CRITICAL**: If EXTEND.md not found, MUST complete first-time setup before ANY other questions or steps. Do NOT proceed to reference images, do NOT ask about content, do NOT ask about type/style — ONLY complete the preferences setup first.
|
||||
|
||||
```bash
|
||||
# macOS, Linux, WSL, Git Bash
|
||||
test -f .baoyu-skills/baoyu-article-illustrator/EXTEND.md && echo "project"
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "xdg"
|
||||
test -f "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "user"
|
||||
```
|
||||
|
||||
```powershell
|
||||
# PowerShell (Windows)
|
||||
if (Test-Path .baoyu-skills/baoyu-article-illustrator/EXTEND.md) { "project" }
|
||||
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
|
||||
if (Test-Path "$xdg/baoyu-skills/baoyu-article-illustrator/EXTEND.md") { "xdg" }
|
||||
if (Test-Path "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md") { "user" }
|
||||
```
|
||||
|
||||
| Result | Action |
|
||||
|--------|--------|
|
||||
| Found | Read, parse, display summary → Continue |
|
||||
| Not found | ⛔ **BLOCKING**: Run first-time setup ONLY ([config/first-time-setup.md](config/first-time-setup.md)) → Complete and save EXTEND.md → Then continue |
|
||||
|
||||
**Supports**: Watermark | Preferred type/style | Custom styles | Language | Output directory
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Setup & Analyze
|
||||
|
||||
### 2.1 Analyze Content
|
||||
|
||||
| Analysis | Description |
|
||||
|----------|-------------|
|
||||
| Content type | Technical / Tutorial / Methodology / Narrative |
|
||||
| Illustration purpose | information / visualization / imagination |
|
||||
| Core arguments | 2-5 main points to visualize |
|
||||
| Visual opportunities | Positions where illustrations add value |
|
||||
| Recommended type | Based on content signals and purpose |
|
||||
| Recommended density | Based on length and complexity |
|
||||
|
||||
### 2.2 Extract Core Arguments
|
||||
|
||||
- Main thesis
|
||||
- Key concepts reader needs
|
||||
- Comparisons/contrasts
|
||||
- Framework/model proposed
|
||||
|
||||
**CRITICAL**: If article uses metaphors (e.g., "电锯切西瓜"), do NOT illustrate literally. Visualize the **underlying concept**.
|
||||
|
||||
### 2.3 Identify Positions
|
||||
|
||||
**Illustrate**:
|
||||
- Core arguments (REQUIRED)
|
||||
- Abstract concepts
|
||||
- Data comparisons
|
||||
- Processes, workflows
|
||||
|
||||
**Do NOT Illustrate**:
|
||||
- Metaphors literally
|
||||
- Decorative scenes
|
||||
- Generic illustrations
|
||||
|
||||
### 2.4 Analyze Reference Images (if provided in Step 1.0)
|
||||
|
||||
For each reference image:
|
||||
|
||||
| Analysis | Description |
|
||||
|----------|-------------|
|
||||
| Visual characteristics | Style, colors, composition |
|
||||
| Content/subject | What the reference depicts |
|
||||
| Suitable positions | Which sections match this reference |
|
||||
| Style match | Which illustration types/styles align |
|
||||
| Usage recommendation | `direct` / `style` / `palette` |
|
||||
|
||||
| Usage | When to Use |
|
||||
|-------|-------------|
|
||||
| `direct` | Reference matches desired output closely |
|
||||
| `style` | Extract visual style characteristics only |
|
||||
| `palette` | Extract color scheme only |
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Confirm Settings ⚠️
|
||||
|
||||
**Do NOT skip.** Use ONE AskUserQuestion call with max 4 questions. **Q1, Q2, Q3 are ALL REQUIRED.**
|
||||
|
||||
### Q1: Preset or Type ⚠️ REQUIRED
|
||||
|
||||
Based on Step 2 content analysis, recommend a preset first (sets both type & style). Look up [style-presets.md](style-presets.md) "Content Type → Preset Recommendations" table.
|
||||
|
||||
- [Recommended preset] — [brief: type + style + why] (Recommended)
|
||||
- [Alternative preset] — [brief]
|
||||
- Or choose type manually: infographic / scene / flowchart / comparison / framework / timeline / mixed
|
||||
|
||||
**If user picks a preset → skip Q3** (type & style both resolved).
|
||||
**If user picks a type → Q3 is REQUIRED.**
|
||||
|
||||
### Q2: Density ⚠️ REQUIRED - DO NOT SKIP
|
||||
- minimal (1-2) - Core concepts only
|
||||
- balanced (3-5) - Major sections
|
||||
- per-section - At least 1 per section/chapter (Recommended)
|
||||
- rich (6+) - Comprehensive coverage
|
||||
|
||||
### Q3: Style ⚠️ REQUIRED (skip if preset chosen in Q1)
|
||||
|
||||
If EXTEND.md has `preferred_style`:
|
||||
- [Custom style name + brief description] (Recommended)
|
||||
- [Top compatible core style 1]
|
||||
- [Top compatible core style 2]
|
||||
- Other (see full Style Gallery)
|
||||
|
||||
If no `preferred_style` (present Core Styles first):
|
||||
- [Best compatible core style] (Recommended)
|
||||
- [Other compatible core style 1]
|
||||
- [Other compatible core style 2]
|
||||
- Other (see full Style Gallery)
|
||||
|
||||
**Core Styles** (simplified selection):
|
||||
|
||||
| Core Style | Maps To | Best For |
|
||||
|------------|---------|----------|
|
||||
| `minimal-flat` | notion | General, knowledge sharing, SaaS |
|
||||
| `sci-fi` | blueprint | AI, frontier tech, system design |
|
||||
| `hand-drawn` | sketch/warm | Relaxed, reflective, casual |
|
||||
| `editorial` | editorial | Processes, data, journalism |
|
||||
| `scene` | warm/watercolor | Narratives, emotional, lifestyle |
|
||||
| `poster` | screen-print | Opinion, editorial, cultural, cinematic |
|
||||
|
||||
Style selection based on Type × Style compatibility matrix (styles.md).
|
||||
Full specs: `styles/<style>.md`
|
||||
|
||||
### Q4: Image Text Language ⚠️ REQUIRED when article language ≠ EXTEND.md `language`
|
||||
|
||||
Detect article language from content. If different from EXTEND.md `language` setting, MUST ask:
|
||||
- Article language (match article content) (Recommended)
|
||||
- EXTEND.md language (user's general preference)
|
||||
|
||||
**Skip only if**: Article language matches EXTEND.md `language`, or EXTEND.md has no `language` setting.
|
||||
|
||||
### Display Reference Usage (if references detected in Step 1.0)
|
||||
|
||||
When presenting outline preview to user, show reference assignments:
|
||||
|
||||
```
|
||||
Reference Images:
|
||||
| Ref | Filename | Recommended Usage |
|
||||
|-----|----------|-------------------|
|
||||
| 01 | 01-ref-diagram.png | direct → Illustration 1, 3 |
|
||||
| 02 | 02-ref-chart.png | palette → Illustration 2 |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Generate Outline
|
||||
|
||||
Save as `outline.md`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
type: infographic
|
||||
density: balanced
|
||||
style: blueprint
|
||||
image_count: 4
|
||||
references: # Only if references provided
|
||||
- ref_id: 01
|
||||
filename: 01-ref-diagram.png
|
||||
description: "Technical diagram showing system architecture"
|
||||
- ref_id: 02
|
||||
filename: 02-ref-chart.png
|
||||
description: "Color chart with brand palette"
|
||||
---
|
||||
|
||||
## Illustration 1
|
||||
|
||||
**Position**: [section] / [paragraph]
|
||||
**Purpose**: [why this helps]
|
||||
**Visual Content**: [what to show]
|
||||
**Type Application**: [how type applies]
|
||||
**References**: [01] # Optional: list ref_ids used
|
||||
**Reference Usage**: direct # direct | style | palette
|
||||
**Filename**: 01-infographic-concept-name.png
|
||||
|
||||
## Illustration 2
|
||||
...
|
||||
```
|
||||
|
||||
**Requirements**:
|
||||
- Each position justified by content needs
|
||||
- Type applied consistently
|
||||
- Style reflected in descriptions
|
||||
- Count matches density
|
||||
- References assigned based on Step 2.4 analysis
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Generate Images
|
||||
|
||||
### 5.1 Create Prompts ⛔ BLOCKING
|
||||
|
||||
**Every illustration MUST have a saved prompt file before generation begins. DO NOT skip this step.**
|
||||
|
||||
For each illustration in the outline:
|
||||
|
||||
1. **Create prompt file**: `prompts/NN-{type}-{slug}.md`
|
||||
2. **Include YAML frontmatter**:
|
||||
```yaml
|
||||
---
|
||||
illustration_id: 01
|
||||
type: infographic
|
||||
style: custom-flat-vector
|
||||
---
|
||||
```
|
||||
3. **Follow type-specific template** from [prompt-construction.md](prompt-construction.md)
|
||||
4. **Prompt quality requirements** (all REQUIRED):
|
||||
- `Layout`: Describe overall composition (grid / radial / hierarchical / left-right / top-down)
|
||||
- `ZONES`: Describe each visual area with specific content, not vague descriptions
|
||||
- `LABELS`: Use **actual numbers, terms, metrics, quotes from the article** — NOT generic placeholders
|
||||
- `COLORS`: Specify hex codes with semantic meaning (e.g., `Coral (#E07A5F) for emphasis`)
|
||||
- `STYLE`: Describe line treatment, texture, mood, character rendering
|
||||
- `ASPECT`: Specify ratio (e.g., `16:9`)
|
||||
5. **Apply defaults**: composition requirements, character rendering, text guidelines, watermark
|
||||
6. **Backup rule**: If prompt file exists, rename to `prompts/NN-{type}-{slug}-backup-YYYYMMDD-HHMMSS.md`
|
||||
|
||||
**Verification** ⛔: Before proceeding to 5.2, confirm ALL prompt files exist:
|
||||
```
|
||||
Prompt Files:
|
||||
- prompts/01-infographic-overview.md ✓
|
||||
- prompts/02-infographic-distillation.md ✓
|
||||
...
|
||||
```
|
||||
|
||||
**DO NOT** pass ad-hoc inline text to `--prompt` without first saving prompt files. The generation command should either use `--promptfiles prompts/NN-{type}-{slug}.md` or read the saved file content for `--prompt`.
|
||||
|
||||
**Execution choice**:
|
||||
- If multiple illustrations already have saved prompt files and the task is now plain generation, prefer `baoyu-image-gen` batch mode (`build-batch.ts` -> `main.ts --batchfile`)
|
||||
- Use subagents only when each illustration still needs separate prompt rewriting, style exploration, or other per-image reasoning before generation
|
||||
|
||||
**CRITICAL - References in Frontmatter**:
|
||||
- Only add `references` field if files ACTUALLY EXIST in `references/` directory
|
||||
- If style/palette was extracted verbally (no file), append info to prompt BODY instead
|
||||
- Before writing frontmatter, verify: `test -f references/NN-ref-{slug}.png`
|
||||
|
||||
### 5.2 Select Generation Skill
|
||||
|
||||
Check available skills. If multiple, ask user.
|
||||
|
||||
### 5.3 Process References ⚠️ REQUIRED if references saved in Step 1.0
|
||||
|
||||
**DO NOT SKIP if user provided reference images.** For each illustration with references:
|
||||
|
||||
1. **VERIFY files exist first**:
|
||||
```bash
|
||||
test -f references/NN-ref-{slug}.png && echo "exists" || echo "MISSING"
|
||||
```
|
||||
- If file MISSING but in frontmatter → ERROR, fix frontmatter or remove references field
|
||||
- If file exists → proceed with processing
|
||||
|
||||
2. Read prompt frontmatter for reference info
|
||||
3. Process based on usage type:
|
||||
|
||||
| Usage | Action | Example |
|
||||
|-------|--------|---------|
|
||||
| `direct` | Add reference path to `--ref` parameter | `--ref references/01-ref-brand.png` |
|
||||
| `style` | Analyze reference, append style traits to prompt | "Style: clean lines, gradient backgrounds..." |
|
||||
| `palette` | Extract colors from reference, append to prompt | "Colors: #E8756D coral, #7ECFC0 mint..." |
|
||||
|
||||
4. Check image generation skill capability:
|
||||
|
||||
| Skill Supports `--ref` | Action |
|
||||
|------------------------|--------|
|
||||
| Yes (e.g., baoyu-image-gen with Google) | Pass reference images via `--ref` |
|
||||
| No | Convert to text description, append to prompt |
|
||||
|
||||
**Verification**: Before generating, confirm reference processing:
|
||||
```
|
||||
Reference Processing:
|
||||
- Illustration 1: using 01-ref-brand.png (direct) ✓
|
||||
- Illustration 2: extracted palette from 02-ref-style.png ✓
|
||||
```
|
||||
|
||||
### 5.4 Apply Watermark (if enabled)
|
||||
|
||||
Add: `Include a subtle watermark "[content]" at [position].`
|
||||
|
||||
### 5.5 Generate
|
||||
|
||||
1. For each illustration:
|
||||
- **Backup rule**: If image file exists, rename to `NN-{type}-{slug}-backup-YYYYMMDD-HHMMSS.md`
|
||||
- If references with `direct` usage: include `--ref` parameter
|
||||
- Generate image
|
||||
2. After each: "Generated X/N"
|
||||
3. On failure: retry once, then log and continue
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Finalize
|
||||
|
||||
### 6.1 Update Article
|
||||
|
||||
Insert after corresponding paragraph:
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
Alt text: concise description in article's language.
|
||||
|
||||
### 6.2 Output Summary
|
||||
|
||||
```
|
||||
Article Illustration Complete!
|
||||
|
||||
Article: [path]
|
||||
Type: [type] | Density: [level] | Style: [style]
|
||||
Location: [directory]
|
||||
Images: X/N generated
|
||||
|
||||
Positions:
|
||||
- 01-xxx.png → After "[Section]"
|
||||
- 02-yyy.png → After "[Section]"
|
||||
|
||||
[If failures]
|
||||
Failed:
|
||||
- NN-zzz.png: [reason]
|
||||
```
|
||||
@@ -0,0 +1,156 @@
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { readdir, readFile, writeFile } from "node:fs/promises";
|
||||
|
||||
type CliArgs = {
|
||||
outlinePath: string | null;
|
||||
promptsDir: string | null;
|
||||
outputPath: string | null;
|
||||
imagesDir: string | null;
|
||||
provider: string;
|
||||
model: string;
|
||||
aspectRatio: string;
|
||||
quality: string;
|
||||
jobs: number | null;
|
||||
help: boolean;
|
||||
};
|
||||
|
||||
type OutlineEntry = {
|
||||
index: number;
|
||||
filename: string;
|
||||
};
|
||||
|
||||
function printUsage(): void {
|
||||
console.log(`Usage:
|
||||
npx -y tsx scripts/build-batch.ts --outline outline.md --prompts prompts --output batch.json --images-dir attachments
|
||||
|
||||
Options:
|
||||
--outline <path> Path to outline.md
|
||||
--prompts <path> Path to prompts directory
|
||||
--output <path> Path to output batch.json
|
||||
--images-dir <path> Directory for generated images
|
||||
--provider <name> Provider for baoyu-image-gen batch tasks (default: replicate)
|
||||
--model <id> Model for baoyu-image-gen batch tasks (default: google/nano-banana-pro)
|
||||
--ar <ratio> Aspect ratio for all tasks (default: 16:9)
|
||||
--quality <level> Quality for all tasks (default: 2k)
|
||||
--jobs <count> Recommended worker count metadata (optional)
|
||||
-h, --help Show help`);
|
||||
}
|
||||
|
||||
function parseArgs(argv: string[]): CliArgs {
|
||||
const args: CliArgs = {
|
||||
outlinePath: null,
|
||||
promptsDir: null,
|
||||
outputPath: null,
|
||||
imagesDir: null,
|
||||
provider: "replicate",
|
||||
model: "google/nano-banana-pro",
|
||||
aspectRatio: "16:9",
|
||||
quality: "2k",
|
||||
jobs: null,
|
||||
help: false,
|
||||
};
|
||||
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const current = argv[i]!;
|
||||
if (current === "--outline") args.outlinePath = argv[++i] ?? null;
|
||||
else if (current === "--prompts") args.promptsDir = argv[++i] ?? null;
|
||||
else if (current === "--output") args.outputPath = argv[++i] ?? null;
|
||||
else if (current === "--images-dir") args.imagesDir = argv[++i] ?? null;
|
||||
else if (current === "--provider") args.provider = argv[++i] ?? args.provider;
|
||||
else if (current === "--model") args.model = argv[++i] ?? args.model;
|
||||
else if (current === "--ar") args.aspectRatio = argv[++i] ?? args.aspectRatio;
|
||||
else if (current === "--quality") args.quality = argv[++i] ?? args.quality;
|
||||
else if (current === "--jobs") {
|
||||
const value = argv[++i];
|
||||
args.jobs = value ? parseInt(value, 10) : null;
|
||||
} else if (current === "--help" || current === "-h") {
|
||||
args.help = true;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
function parseOutline(content: string): OutlineEntry[] {
|
||||
const entries: OutlineEntry[] = [];
|
||||
const blocks = content.split(/^## Illustration\s+/m).slice(1);
|
||||
|
||||
for (const block of blocks) {
|
||||
const indexMatch = block.match(/^(\d+)/);
|
||||
const filenameMatch = block.match(/\*\*Filename\*\*:\s*(.+)/);
|
||||
if (indexMatch && filenameMatch) {
|
||||
entries.push({
|
||||
index: parseInt(indexMatch[1]!, 10),
|
||||
filename: filenameMatch[1]!.trim(),
|
||||
});
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
async function findPromptFile(promptsDir: string, entry: OutlineEntry): Promise<string | null> {
|
||||
const files = await readdir(promptsDir);
|
||||
const prefix = String(entry.index).padStart(2, "0");
|
||||
const match = files.find((f) => f.startsWith(prefix) && f.endsWith(".md"));
|
||||
return match ? path.join(promptsDir, match) : null;
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
if (args.help) {
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.outlinePath) {
|
||||
console.error("Error: --outline is required");
|
||||
process.exit(1);
|
||||
}
|
||||
if (!args.promptsDir) {
|
||||
console.error("Error: --prompts is required");
|
||||
process.exit(1);
|
||||
}
|
||||
if (!args.outputPath) {
|
||||
console.error("Error: --output is required");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const outlineContent = await readFile(args.outlinePath, "utf8");
|
||||
const entries = parseOutline(outlineContent);
|
||||
|
||||
if (entries.length === 0) {
|
||||
console.error("No illustration entries found in outline.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const tasks = [];
|
||||
for (const entry of entries) {
|
||||
const promptFile = await findPromptFile(args.promptsDir, entry);
|
||||
if (!promptFile) {
|
||||
console.error(`Warning: No prompt file found for illustration ${entry.index}, skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const imageDir = args.imagesDir ?? path.dirname(args.outputPath);
|
||||
tasks.push({
|
||||
id: `illustration-${String(entry.index).padStart(2, "0")}`,
|
||||
promptFiles: [promptFile],
|
||||
image: path.join(imageDir, entry.filename),
|
||||
provider: args.provider,
|
||||
model: args.model,
|
||||
ar: args.aspectRatio,
|
||||
quality: args.quality,
|
||||
});
|
||||
}
|
||||
|
||||
const output: Record<string, unknown> = { tasks };
|
||||
if (args.jobs) output.jobs = args.jobs;
|
||||
|
||||
await writeFile(args.outputPath, JSON.stringify(output, null, 2) + "\n");
|
||||
console.log(`Batch file written: ${args.outputPath} (${tasks.length} tasks)`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
});
|
||||
+65
-20
@@ -1,6 +1,14 @@
|
||||
---
|
||||
name: baoyu-comic
|
||||
description: Knowledge comic creator supporting multiple art styles and tones. Creates original educational comics with detailed panel layouts and sequential image generation. Use when user asks to create "知识漫画", "教育漫画", "biography comic", "tutorial comic", or "Logicomix-style comic".
|
||||
version: 1.56.1
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-comic
|
||||
requires:
|
||||
anyBins:
|
||||
- bun
|
||||
- npx
|
||||
---
|
||||
|
||||
# Knowledge Comic Creator
|
||||
@@ -104,9 +112,10 @@ Details: [references/auto-selection.md](references/auto-selection.md)
|
||||
**Important**: All scripts are located in the `scripts/` subdirectory of this skill.
|
||||
|
||||
**Agent Execution Instructions**:
|
||||
1. Determine this SKILL.md file's directory path as `SKILL_DIR`
|
||||
2. Script path = `${SKILL_DIR}/scripts/<script-name>.ts`
|
||||
3. Replace all `${SKILL_DIR}` in this document with the actual path
|
||||
1. Determine this SKILL.md file's directory path as `{baseDir}`
|
||||
2. Script path = `{baseDir}/scripts/<script-name>.ts`
|
||||
3. Replace all `{baseDir}` in this document with the actual path
|
||||
4. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun
|
||||
|
||||
**Script Reference**:
|
||||
| Script | Purpose |
|
||||
@@ -153,7 +162,11 @@ Technical terms remain in English.
|
||||
|
||||
```
|
||||
Comic Progress:
|
||||
- [ ] Step 1: Setup & Analyze (1.1 Preferences, 1.2 Analyze, 1.3 Check existing)
|
||||
- [ ] Step 1: Setup & Analyze
|
||||
- [ ] 1.1 Preferences (EXTEND.md) ⛔ BLOCKING
|
||||
- [ ] Found → load preferences → continue
|
||||
- [ ] Not found → run first-time setup → MUST complete before other steps
|
||||
- [ ] 1.2 Analyze, 1.3 Check existing
|
||||
- [ ] Step 2: Confirmation - Style & options ⚠️ REQUIRED
|
||||
- [ ] Step 3: Generate storyboard + characters
|
||||
- [ ] Step 4: Review outline (conditional)
|
||||
@@ -169,14 +182,22 @@ Comic Progress:
|
||||
### Flow
|
||||
|
||||
```
|
||||
Input → Preferences → Analyze → [Check Existing?] → [Confirm: Style + Reviews] → Storyboard → [Review?] → Prompts → [Review?] → Images → PDF → Complete
|
||||
Input → [Preferences] ─┬─ Found → Continue
|
||||
│
|
||||
└─ Not found → First-Time Setup ⛔ BLOCKING
|
||||
│
|
||||
└─ Complete setup → Save EXTEND.md → Continue
|
||||
│
|
||||
┌─────────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
Analyze → [Check Existing?] → [Confirm: Style + Reviews] → Storyboard → [Review?] → Prompts → [Review?] → Images → PDF → Complete
|
||||
```
|
||||
|
||||
### Step Summary
|
||||
|
||||
| Step | Action | Key Output |
|
||||
|------|--------|------------|
|
||||
| 1.1 | Load EXTEND.md preferences | Config loaded |
|
||||
| 1.1 | Load EXTEND.md preferences ⛔ BLOCKING if not found | Config loaded |
|
||||
| 1.2 | Analyze content | `analysis.md` |
|
||||
| 1.3 | Check existing directory | Handle conflicts |
|
||||
| 2 | Confirm style, focus, audience, reviews | User preferences |
|
||||
@@ -194,12 +215,18 @@ Input → Preferences → Analyze → [Check Existing?] → [Confirm: Style + Re
|
||||
**Character reference is MANDATORY for visual consistency.**
|
||||
|
||||
**7.1 Generate character sheet first**:
|
||||
```bash
|
||||
# Use Reference Sheet Prompt from characters/characters.md
|
||||
npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
|
||||
--promptfiles characters/characters.md \
|
||||
--image characters/characters.png --ar 4:3
|
||||
```
|
||||
- **Backup rule**: If `characters/characters.png` exists, rename to `characters/characters-backup-YYYYMMDD-HHMMSS.png`
|
||||
- Invoke an installed image generation skill such as `baoyu-image-gen`
|
||||
- Read that skill's `SKILL.md` and follow its documented interface rather than calling its scripts directly
|
||||
- Use `characters/characters.md` as the prompt-file input
|
||||
- Save output to `characters/characters.png`
|
||||
- Use aspect ratio `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**:
|
||||
|
||||
@@ -208,23 +235,31 @@ npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
|
||||
| Supports `--ref` | Pass `characters/characters.png` with EVERY page |
|
||||
| No `--ref` support | Prepend character descriptions to EVERY prompt file |
|
||||
|
||||
```bash
|
||||
# Example: ALWAYS include --ref for consistency
|
||||
npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
|
||||
--promptfiles prompts/01-page-xxx.md \
|
||||
--image 01-page-xxx.png --ar 3:4 \
|
||||
--ref characters/characters.png
|
||||
```
|
||||
**Backup rules for page generation**:
|
||||
- If prompt file exists: rename to `prompts/NN-{cover|page}-[slug]-backup-YYYYMMDD-HHMMSS.md`
|
||||
- If image file exists: rename to `NN-{cover|page}-[slug]-backup-YYYYMMDD-HHMMSS.png`
|
||||
- Invoke the installed image generation skill for each page
|
||||
- Use `prompts/01-page-xxx.md` as the prompt-file input
|
||||
- Save output to `01-page-xxx.png`
|
||||
- Use aspect ratio `3:4`
|
||||
- If the chosen skill supports reference images, pass `characters/characters.png` as `--ref`
|
||||
|
||||
**Full workflow details**: [references/workflow.md](references/workflow.md)
|
||||
|
||||
### EXTEND.md Paths
|
||||
### EXTEND.md Paths ⛔ BLOCKING
|
||||
|
||||
**CRITICAL**: If EXTEND.md not found, MUST complete first-time setup before ANY other questions or steps. Do NOT proceed to content analysis, do NOT ask about art style, do NOT ask about tone — ONLY complete the preferences setup first.
|
||||
|
||||
| Path | Location |
|
||||
|------|----------|
|
||||
| `.baoyu-skills/baoyu-comic/EXTEND.md` | Project directory |
|
||||
| `$HOME/.baoyu-skills/baoyu-comic/EXTEND.md` | User home |
|
||||
|
||||
| Result | Action |
|
||||
|--------|--------|
|
||||
| Found | Read, parse, display summary → Continue |
|
||||
| Not found | ⛔ **BLOCKING**: Run first-time setup ONLY ([references/config/first-time-setup.md](references/config/first-time-setup.md)) → Complete and save EXTEND.md → Then continue |
|
||||
|
||||
**EXTEND.md Supports**: Watermark | Preferred art/tone/layout | Custom style definitions | Character presets | Language preference
|
||||
|
||||
Schema: [references/config/preferences-schema.md](references/config/preferences-schema.md)
|
||||
@@ -253,6 +288,16 @@ Schema: [references/config/preferences-schema.md](references/config/preferences-
|
||||
- [config/first-time-setup.md](references/config/first-time-setup.md) - First-time setup
|
||||
- [config/watermark-guide.md](references/config/watermark-guide.md) - Watermark configuration
|
||||
|
||||
## Page Modification
|
||||
|
||||
| Action | Steps |
|
||||
|--------|-------|
|
||||
| **Edit** | **Update prompt file FIRST** → `--regenerate N` → Regenerate PDF |
|
||||
| **Add** | Create prompt at position → Generate with character ref → Renumber subsequent → Update storyboard → Regenerate PDF |
|
||||
| **Delete** | Remove files → Renumber subsequent → Update storyboard → Regenerate PDF |
|
||||
|
||||
**IMPORTANT**: When updating pages, ALWAYS update the prompt file (`prompts/NN-{cover|page}-[slug].md`) FIRST before regenerating. This ensures changes are documented and reproducible.
|
||||
|
||||
## Notes
|
||||
|
||||
- Image generation: 10-30 seconds per page
|
||||
|
||||
@@ -9,6 +9,14 @@ description: First-time setup flow for baoyu-comic preferences
|
||||
|
||||
When no EXTEND.md is found, guide user through preference setup.
|
||||
|
||||
**⛔ BLOCKING OPERATION**: This setup MUST complete before ANY other workflow steps. Do NOT:
|
||||
- Ask about content/source material
|
||||
- Ask about art style or tone
|
||||
- Ask about layout preferences
|
||||
- Proceed to content analysis
|
||||
|
||||
ONLY ask the questions in this setup flow, save EXTEND.md, then continue.
|
||||
|
||||
## Setup Flow
|
||||
|
||||
```
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -34,16 +34,23 @@ Input → Preferences → Analyze → [Check Existing?] → [Confirm 1: Style +
|
||||
|
||||
### 1.1 Load Preferences (EXTEND.md)
|
||||
|
||||
Use Bash to check EXTEND.md existence (priority order):
|
||||
Check EXTEND.md existence (priority order):
|
||||
|
||||
```bash
|
||||
# Check project-level first
|
||||
# macOS, Linux, WSL, Git Bash
|
||||
test -f .baoyu-skills/baoyu-comic/EXTEND.md && echo "project"
|
||||
|
||||
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-comic/EXTEND.md" && echo "xdg"
|
||||
test -f "$HOME/.baoyu-skills/baoyu-comic/EXTEND.md" && echo "user"
|
||||
```
|
||||
|
||||
```powershell
|
||||
# PowerShell (Windows)
|
||||
if (Test-Path .baoyu-skills/baoyu-comic/EXTEND.md) { "project" }
|
||||
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
|
||||
if (Test-Path "$xdg/baoyu-skills/baoyu-comic/EXTEND.md") { "xdg" }
|
||||
if (Test-Path "$HOME/.baoyu-skills/baoyu-comic/EXTEND.md") { "user" }
|
||||
```
|
||||
|
||||
| Path | Location |
|
||||
|------|----------|
|
||||
| `.baoyu-skills/baoyu-comic/EXTEND.md` | Project directory |
|
||||
@@ -84,6 +91,7 @@ Read source content, save it if needed, and perform deep analysis.
|
||||
1. **Save source content** (if not already a file):
|
||||
- If user provides a file path: use as-is
|
||||
- If user pastes content: save to `source.md` in target directory
|
||||
- **Backup rule**: If `source.md` exists, rename to `source-backup-YYYYMMDD-HHMMSS.md`
|
||||
2. Read source content
|
||||
3. **Deep analysis** following `analysis-framework.md`:
|
||||
- Target audience identification
|
||||
@@ -329,6 +337,7 @@ Create image generation prompts for all pages.
|
||||
1. Create prompt following art style + tone guidelines
|
||||
2. Include character visual descriptions for consistency
|
||||
3. Save to `prompts/NN-{cover|page}-[slug].md`
|
||||
- **Backup rule**: If prompt file exists, rename to `prompts/NN-{cover|page}-[slug]-backup-YYYYMMDD-HHMMSS.md`
|
||||
|
||||
**Prompt File Format**:
|
||||
```markdown
|
||||
@@ -405,8 +414,9 @@ With confirmed prompts from Step 5/6:
|
||||
### 7.1 Generate Character Reference Sheet (first)
|
||||
|
||||
1. Use Reference Sheet Prompt from `characters/characters.md`
|
||||
2. Generate → `characters/characters.png`
|
||||
3. This ensures visual consistency for all subsequent pages
|
||||
2. **Backup rule**: If `characters/characters.png` exists, rename to `characters/characters-backup-YYYYMMDD-HHMMSS.png`
|
||||
3. Generate → `characters/characters.png`
|
||||
4. This ensures visual consistency for all subsequent pages
|
||||
|
||||
### 7.2 Generate Comic Pages
|
||||
|
||||
@@ -426,14 +436,12 @@ With confirmed prompts from Step 5/6:
|
||||
|
||||
**Strategy A: Using `--ref` parameter** (e.g., baoyu-image-gen)
|
||||
|
||||
```bash
|
||||
# Each page generation MUST include --ref
|
||||
npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
|
||||
--promptfiles prompts/01-page-xxx.md \
|
||||
--image 01-page-xxx.png \
|
||||
--ar 3:4 \
|
||||
--ref characters/characters.png
|
||||
```
|
||||
- Read the chosen image generation skill's `SKILL.md`
|
||||
- Invoke that installed skill via its documented interface, not by calling its scripts directly
|
||||
- For every page, use `prompts/01-page-xxx.md` as the prompt-file input
|
||||
- Save output to `01-page-xxx.png`
|
||||
- Use aspect ratio `3:4`
|
||||
- Pass `characters/characters.png` as `--ref` on every page generation
|
||||
|
||||
**Strategy B: Embedding character descriptions in prompt**
|
||||
|
||||
@@ -453,9 +461,10 @@ When skill does NOT support reference images, create combined prompt files:
|
||||
|
||||
**For each page (cover + pages)**:
|
||||
1. Read prompt from `prompts/NN-{cover|page}-[slug].md`
|
||||
2. Generate image using Strategy A or B (based on skill capability)
|
||||
3. Save to `NN-{cover|page}-[slug].png`
|
||||
4. Report progress after each generation: "Generated X/N: [page title]"
|
||||
2. **Backup rule**: If image file exists, rename to `NN-{cover|page}-[slug]-backup-YYYYMMDD-HHMMSS.png`
|
||||
3. Generate image using Strategy A or B (based on skill capability)
|
||||
4. Save to `NN-{cover|page}-[slug].png`
|
||||
5. Report progress after each generation: "Generated X/N: [page title]"
|
||||
|
||||
**Session Management**:
|
||||
If image generation skill supports `--sessionId`:
|
||||
@@ -470,7 +479,7 @@ If image generation skill supports `--sessionId`:
|
||||
After all images generated:
|
||||
|
||||
```bash
|
||||
npx -y bun ${SKILL_DIR}/scripts/merge-to-pdf.ts <comic-dir>
|
||||
${BUN_X} {baseDir}/scripts/merge-to-pdf.ts <comic-dir>
|
||||
```
|
||||
|
||||
Creates `{topic-slug}.pdf` with all pages as full-page images.
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
---
|
||||
name: baoyu-compress-image
|
||||
description: Compresses images to WebP (default) or PNG with automatic tool selection. Use when user asks to "compress image", "optimize image", "convert to webp", or reduce image file size.
|
||||
version: 1.56.1
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-compress-image
|
||||
requires:
|
||||
anyBins:
|
||||
- bun
|
||||
- npx
|
||||
---
|
||||
|
||||
# Image Compressor
|
||||
@@ -9,7 +17,7 @@ Compresses images using best available tool (sips → cwebp → ImageMagick →
|
||||
|
||||
## Script Directory
|
||||
|
||||
Scripts in `scripts/` subdirectory. Replace `${SKILL_DIR}` with this SKILL.md's directory path.
|
||||
Scripts in `scripts/` subdirectory. `{baseDir}` = this SKILL.md's directory path. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun. Replace `{baseDir}` and `${BUN_X}` with actual values.
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
@@ -17,16 +25,23 @@ Scripts in `scripts/` subdirectory. Replace `${SKILL_DIR}` with this SKILL.md's
|
||||
|
||||
## Preferences (EXTEND.md)
|
||||
|
||||
Use Bash to check EXTEND.md existence (priority order):
|
||||
Check EXTEND.md existence (priority order):
|
||||
|
||||
```bash
|
||||
# Check project-level first
|
||||
# macOS, Linux, WSL, Git Bash
|
||||
test -f .baoyu-skills/baoyu-compress-image/EXTEND.md && echo "project"
|
||||
|
||||
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-compress-image/EXTEND.md" && echo "xdg"
|
||||
test -f "$HOME/.baoyu-skills/baoyu-compress-image/EXTEND.md" && echo "user"
|
||||
```
|
||||
|
||||
```powershell
|
||||
# PowerShell (Windows)
|
||||
if (Test-Path .baoyu-skills/baoyu-compress-image/EXTEND.md) { "project" }
|
||||
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
|
||||
if (Test-Path "$xdg/baoyu-skills/baoyu-compress-image/EXTEND.md") { "xdg" }
|
||||
if (Test-Path "$HOME/.baoyu-skills/baoyu-compress-image/EXTEND.md") { "user" }
|
||||
```
|
||||
|
||||
┌────────────────────────────────────────────────────────┬───────────────────┐
|
||||
│ Path │ Location │
|
||||
├────────────────────────────────────────────────────────┼───────────────────┤
|
||||
@@ -48,7 +63,7 @@ test -f "$HOME/.baoyu-skills/baoyu-compress-image/EXTEND.md" && echo "user"
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts <input> [options]
|
||||
${BUN_X} {baseDir}/scripts/main.ts <input> [options]
|
||||
```
|
||||
|
||||
## Options
|
||||
@@ -67,16 +82,16 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts <input> [options]
|
||||
|
||||
```bash
|
||||
# Single file → WebP (replaces original)
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png
|
||||
${BUN_X} {baseDir}/scripts/main.ts image.png
|
||||
|
||||
# Keep PNG format
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png -f png --keep
|
||||
${BUN_X} {baseDir}/scripts/main.ts image.png -f png --keep
|
||||
|
||||
# Directory recursive
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts ./images/ -r -q 75
|
||||
${BUN_X} {baseDir}/scripts/main.ts ./images/ -r -q 75
|
||||
|
||||
# JSON output
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png --json
|
||||
${BUN_X} {baseDir}/scripts/main.ts image.png --json
|
||||
```
|
||||
|
||||
**Output**:
|
||||
|
||||
@@ -147,7 +147,9 @@ async function processFile(
|
||||
const outputSize = statSync(tempOutput).size;
|
||||
|
||||
if (!opts.keep && absInput !== output) {
|
||||
unlinkSync(absInput);
|
||||
const ext = extname(absInput);
|
||||
const base = absInput.slice(0, -ext.length);
|
||||
renameSync(absInput, `${base}_original${ext}`);
|
||||
}
|
||||
renameSync(tempOutput, output);
|
||||
|
||||
|
||||
+138
-389
@@ -1,33 +1,36 @@
|
||||
---
|
||||
name: baoyu-cover-image
|
||||
description: Generates article cover images with 20 hand-drawn styles and auto-style selection. Supports cinematic (2.35:1), widescreen (16:9), and square (1:1) aspects. Use when user asks to "generate cover image", "create article cover", "make cover", or mentions "封面图".
|
||||
description: Generates article cover images with 5 dimensions (type, palette, rendering, text, mood) combining 10 color palettes and 7 rendering styles. Supports cinematic (2.35:1), widescreen (16:9), and square (1:1) aspects. Use when user asks to "generate cover image", "create article cover", or "make cover".
|
||||
version: 1.56.1
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-cover-image
|
||||
---
|
||||
|
||||
# Cover Image Generator
|
||||
|
||||
Generate elegant cover images for articles with multiple style options.
|
||||
Generate elegant cover images for articles with 5-dimensional customization.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Auto-select style and aspect based on content
|
||||
# Auto-select dimensions based on content
|
||||
/baoyu-cover-image path/to/article.md
|
||||
|
||||
# Specify style
|
||||
# Quick mode: skip confirmation
|
||||
/baoyu-cover-image article.md --quick
|
||||
|
||||
# Specify dimensions
|
||||
/baoyu-cover-image article.md --type conceptual --palette warm --rendering flat-vector
|
||||
|
||||
# Style presets (shorthand for palette + rendering)
|
||||
/baoyu-cover-image article.md --style blueprint
|
||||
|
||||
# Specify aspect ratio
|
||||
/baoyu-cover-image article.md --aspect 16:9
|
||||
|
||||
# Visual only (no title text)
|
||||
/baoyu-cover-image article.md --no-title
|
||||
# With reference images
|
||||
/baoyu-cover-image article.md --ref style-ref.png
|
||||
|
||||
# Direct content input
|
||||
/baoyu-cover-image
|
||||
[paste content]
|
||||
|
||||
# Direct input with options
|
||||
/baoyu-cover-image --style notion --aspect 1:1
|
||||
/baoyu-cover-image --palette mono --aspect 1:1 --quick
|
||||
[paste content]
|
||||
```
|
||||
|
||||
@@ -35,147 +38,80 @@ Generate elegant cover images for articles with multiple style options.
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--type <name>` | Cover type (see Type Gallery) |
|
||||
| `--style <name>` | Cover style (see Style Gallery) |
|
||||
| `--aspect <ratio>` | 2.35:1 (default), 16:9, 1:1 |
|
||||
| `--type <name>` | hero, conceptual, typography, metaphor, scene, minimal |
|
||||
| `--palette <name>` | warm, elegant, cool, dark, earth, vivid, pastel, mono, retro, duotone |
|
||||
| `--rendering <name>` | flat-vector, hand-drawn, painterly, digital, pixel, chalk, screen-print |
|
||||
| `--style <name>` | Preset shorthand (see [Style Presets](references/style-presets.md)) |
|
||||
| `--text <level>` | none, title-only, title-subtitle, text-rich |
|
||||
| `--mood <level>` | subtle, balanced, bold |
|
||||
| `--font <name>` | clean, handwritten, serif, display |
|
||||
| `--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 |
|
||||
| `--ref <files...>` | Reference images for style/composition guidance |
|
||||
|
||||
## Two Dimensions
|
||||
## Five Dimensions
|
||||
|
||||
| Dimension | Controls | Examples |
|
||||
|-----------|----------|----------|
|
||||
| **Type** | Visual composition, information structure | hero, conceptual, typography, metaphor, scene, minimal |
|
||||
| **Style** | Visual aesthetics, colors, mood | elegant, blueprint, notion, warm, minimal, watercolor |
|
||||
| Dimension | Values | Default |
|
||||
|-----------|--------|---------|
|
||||
| **Type** | hero, conceptual, typography, metaphor, scene, minimal | auto |
|
||||
| **Palette** | warm, elegant, cool, dark, earth, vivid, pastel, mono, retro, duotone | auto |
|
||||
| **Rendering** | flat-vector, hand-drawn, painterly, digital, pixel, chalk, screen-print | auto |
|
||||
| **Text** | none, title-only, title-subtitle, text-rich | title-only |
|
||||
| **Mood** | subtle, balanced, bold | balanced |
|
||||
| **Font** | clean, handwritten, serif, display | clean |
|
||||
|
||||
Type × Style can be freely combined. Example: `--type conceptual --style blueprint` creates technical concept visualization with schematic aesthetics.
|
||||
Auto-selection rules: [references/auto-selection.md](references/auto-selection.md)
|
||||
|
||||
## Type Gallery
|
||||
## Galleries
|
||||
|
||||
| Type | Description | Best For |
|
||||
|------|-------------|----------|
|
||||
| `hero` | Large visual impact, title overlay | Product launch, brand promotion, major announcements |
|
||||
| `conceptual` | Concept visualization, abstract core ideas | Technical articles, methodology, architecture design |
|
||||
| `typography` | Text-focused layout, prominent title | Opinion pieces, quotes, insights |
|
||||
| `metaphor` | Visual metaphor, concrete expressing abstract | Philosophy, growth, personal development |
|
||||
| `scene` | Atmospheric scene, narrative feel | Stories, travel, lifestyle |
|
||||
| `minimal` | Minimalist composition, generous whitespace | Zen, focus, core concepts |
|
||||
**Types**: hero, conceptual, typography, metaphor, scene, minimal
|
||||
→ Details: [references/types.md](references/types.md)
|
||||
|
||||
## Auto Type Selection
|
||||
**Palettes**: warm, elegant, cool, dark, earth, vivid, pastel, mono, retro, duotone
|
||||
→ Details: [references/palettes/](references/palettes/)
|
||||
|
||||
When `--type` is omitted, select based on content signals:
|
||||
**Renderings**: flat-vector, hand-drawn, painterly, digital, pixel, chalk, screen-print
|
||||
→ Details: [references/renderings/](references/renderings/)
|
||||
|
||||
| Signals | Type |
|
||||
|---------|------|
|
||||
| Product, launch, announcement, release, reveal | `hero` |
|
||||
| Architecture, framework, system, API, technical, model | `conceptual` |
|
||||
| Quote, opinion, insight, thought, headline, statement | `typography` |
|
||||
| Philosophy, growth, abstract, meaning, reflection | `metaphor` |
|
||||
| Story, journey, travel, lifestyle, experience, narrative | `scene` |
|
||||
| Zen, focus, essential, core, simple, pure | `minimal` |
|
||||
**Text Levels**: none (pure visual) | title-only (default) | title-subtitle | text-rich (with tags)
|
||||
→ Details: [references/dimensions/text.md](references/dimensions/text.md)
|
||||
|
||||
## Style Gallery
|
||||
**Mood Levels**: subtle (low contrast) | balanced (default) | bold (high contrast)
|
||||
→ Details: [references/dimensions/mood.md](references/dimensions/mood.md)
|
||||
|
||||
| Style | Description |
|
||||
|-------|-------------|
|
||||
| `elegant` (default) | Refined, sophisticated |
|
||||
| `blueprint` | Technical schematics |
|
||||
| `bold-editorial` | Magazine impact |
|
||||
| `chalkboard` | Chalk on blackboard |
|
||||
| `dark-atmospheric` | Cinematic dark mode |
|
||||
| `editorial-infographic` | Visual storytelling |
|
||||
| `fantasy-animation` | Ghibli/Disney inspired |
|
||||
| `flat-doodle` | Pastel, cute shapes |
|
||||
| `intuition-machine` | Technical, bilingual |
|
||||
| `minimal` | Ultra-clean, zen |
|
||||
| `nature` | Organic, earthy |
|
||||
| `notion` | SaaS dashboard |
|
||||
| `pixel-art` | Retro 8-bit |
|
||||
| `playful` | Fun, whimsical |
|
||||
| `retro` | Halftone, vintage |
|
||||
| `sketch-notes` | Hand-drawn, warm |
|
||||
| `vector-illustration` | Flat vector |
|
||||
| `vintage` | Aged, expedition |
|
||||
| `warm` | Friendly, human |
|
||||
| `watercolor` | Soft hand-painted |
|
||||
|
||||
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:
|
||||
|
||||
| Signals | Style |
|
||||
|---------|-------|
|
||||
| Architecture, system design | `blueprint` |
|
||||
| Product launch, marketing | `bold-editorial` |
|
||||
| Education, tutorial | `chalkboard` |
|
||||
| Entertainment, premium | `dark-atmospheric` |
|
||||
| Tech explainer, research | `editorial-infographic` |
|
||||
| Fantasy, children | `fantasy-animation` |
|
||||
| Technical docs, bilingual | `intuition-machine` |
|
||||
| Personal story, emotion | `warm` |
|
||||
| Zen, focus, essential | `minimal` |
|
||||
| Fun, beginner, casual | `playful` |
|
||||
| Nature, wellness, eco | `nature` |
|
||||
| SaaS, dashboard | `notion` |
|
||||
| Workflow, productivity | `flat-doodle` |
|
||||
| Gaming, retro tech | `pixel-art` |
|
||||
| Knowledge sharing | `sketch-notes` |
|
||||
| Creative proposals | `vector-illustration` |
|
||||
| History, exploration | `vintage` |
|
||||
| Lifestyle, travel | `watercolor` |
|
||||
| Business, professional | `elegant` |
|
||||
**Fonts**: clean (sans-serif) | handwritten | serif | display (bold decorative)
|
||||
→ Details: [references/dimensions/font.md](references/dimensions/font.md)
|
||||
|
||||
## File Structure
|
||||
|
||||
Each session creates an independent directory named by content slug:
|
||||
Output directory per `default_output_dir` preference:
|
||||
- `same-dir`: `{article-dir}/`
|
||||
- `imgs-subdir`: `{article-dir}/imgs/`
|
||||
- `independent` (default): `cover-image/{topic-slug}/`
|
||||
|
||||
```
|
||||
cover-image/{topic-slug}/
|
||||
├── source-{slug}.{ext} # Source files (text, images, etc.)
|
||||
<output-dir>/
|
||||
├── source-{slug}.{ext} # Source files
|
||||
├── refs/ # Reference images (if provided)
|
||||
│ ├── ref-01-{slug}.{ext}
|
||||
│ └── ref-01-{slug}.md # Description file
|
||||
├── prompts/cover.md # Generation prompt
|
||||
└── cover.png # Output image
|
||||
```
|
||||
|
||||
**Slug Generation**:
|
||||
1. Extract main topic from content (2-4 words, kebab-case)
|
||||
2. Example: "The Future of AI" → `future-of-ai`
|
||||
|
||||
**Conflict Resolution**:
|
||||
If `cover-image/{topic-slug}/` already exists:
|
||||
- Append timestamp: `{topic-slug}-YYYYMMDD-HHMMSS`
|
||||
- Example: `ai-future` exists → `ai-future-20260118-143052`
|
||||
|
||||
**Source Files**:
|
||||
Copy all sources with naming `source-{slug}.{ext}`:
|
||||
- `source-article.md`, `source-reference.png`, etc.
|
||||
- Multiple sources supported: text, images, files from conversation
|
||||
**Slug**: 2-4 words, kebab-case. Conflict: append `-YYYYMMDD-HHMMSS`
|
||||
|
||||
## Workflow
|
||||
|
||||
### Progress Checklist
|
||||
|
||||
Copy and track progress:
|
||||
|
||||
```
|
||||
Cover Image Progress:
|
||||
- [ ] Step 0: Check preferences (EXTEND.md) ⚠️ REQUIRED if not found
|
||||
- [ ] Step 1: Analyze content
|
||||
- [ ] Step 2: Confirm options (type + style + aspect) ⚠️ REQUIRED
|
||||
- [ ] Step 0: Check preferences (EXTEND.md) ⛔ BLOCKING
|
||||
- [ ] Step 1: Analyze content + save refs + determine output dir
|
||||
- [ ] Step 2: Confirm options (6 dimensions) ⚠️ unless --quick
|
||||
- [ ] Step 3: Create prompt
|
||||
- [ ] Step 4: Generate image
|
||||
- [ ] Step 5: Completion report
|
||||
@@ -184,266 +120,77 @@ Cover Image Progress:
|
||||
### Flow
|
||||
|
||||
```
|
||||
Input → [Step 0: Preferences/Setup] → Analyze → [Confirm: Type + Style + Aspect] → Prompt → Generate → Complete
|
||||
Input → [Step 0: Preferences] ─┬─ Found → Continue
|
||||
└─ Not found → First-Time Setup ⛔ BLOCKING → Save EXTEND.md → Continue
|
||||
↓
|
||||
Analyze + Save Refs → [Output Dir] → [Confirm: 6 Dimensions] → Prompt → Generate → Complete
|
||||
↓
|
||||
(skip if --quick or all specified)
|
||||
```
|
||||
|
||||
### Step 0: Load Preferences (EXTEND.md) ⚠️
|
||||
|
||||
**Purpose**: Load user preferences or run first-time setup. **Do NOT skip setup if EXTEND.md not found.**
|
||||
|
||||
Use Bash to check EXTEND.md existence (priority order):
|
||||
### Step 0: Load Preferences ⛔ BLOCKING
|
||||
|
||||
Check EXTEND.md existence (priority: project → user):
|
||||
```bash
|
||||
# Check project-level first
|
||||
# macOS, Linux, WSL, Git Bash
|
||||
test -f .baoyu-skills/baoyu-cover-image/EXTEND.md && echo "project"
|
||||
|
||||
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-cover-image/EXTEND.md" && echo "xdg"
|
||||
test -f "$HOME/.baoyu-skills/baoyu-cover-image/EXTEND.md" && echo "user"
|
||||
```
|
||||
|
||||
┌──────────────────────────────────────────────────┬───────────────────┐
|
||||
│ Path │ Location │
|
||||
├──────────────────────────────────────────────────┼───────────────────┤
|
||||
│ .baoyu-skills/baoyu-cover-image/EXTEND.md │ Project directory │
|
||||
├──────────────────────────────────────────────────┼───────────────────┤
|
||||
│ $HOME/.baoyu-skills/baoyu-cover-image/EXTEND.md │ User home │
|
||||
└──────────────────────────────────────────────────┴───────────────────┘
|
||||
|
||||
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
|
||||
│ Result │ Action │
|
||||
├───────────┼───────────────────────────────────────────────────────────────────────────┤
|
||||
│ Found │ Read, parse, display preferences summary (see below) → Continue to Step 1 │
|
||||
├───────────┼───────────────────────────────────────────────────────────────────────────┤
|
||||
│ Not found │ ⚠️ MUST run first-time setup (see below) → Then continue to Step 1 │
|
||||
└───────────┴───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
**Preferences Summary** (when EXTEND.md found):
|
||||
|
||||
Display loaded preferences:
|
||||
|
||||
```
|
||||
Preferences loaded from [project/user]:
|
||||
• Watermark: [enabled/disabled] [content if enabled]
|
||||
• Type: [preferred_type or "auto"]
|
||||
• Style: [preferred_style or "auto"]
|
||||
• Aspect: [default_aspect]
|
||||
• Language: [language or "auto"]
|
||||
```powershell
|
||||
# PowerShell (Windows)
|
||||
if (Test-Path .baoyu-skills/baoyu-cover-image/EXTEND.md) { "project" }
|
||||
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
|
||||
if (Test-Path "$xdg/baoyu-skills/baoyu-cover-image/EXTEND.md") { "xdg" }
|
||||
if (Test-Path "$HOME/.baoyu-skills/baoyu-cover-image/EXTEND.md") { "user" }
|
||||
```
|
||||
|
||||
**First-Time Setup** (when EXTEND.md not found):
|
||||
| Result | Action |
|
||||
|--------|--------|
|
||||
| Found | Load, display summary → Continue |
|
||||
| Not found | ⛔ Run first-time setup ([references/config/first-time-setup.md](references/config/first-time-setup.md)) → Save → Continue |
|
||||
|
||||
**Language**: Use user's input language or saved language preference.
|
||||
|
||||
Use AskUserQuestion with ALL questions in ONE call:
|
||||
|
||||
**Q1: Watermark**
|
||||
```yaml
|
||||
header: "Watermark"
|
||||
question: "Watermark text for generated cover images?"
|
||||
options:
|
||||
- label: "No watermark (Recommended)"
|
||||
description: "Clean covers, can enable later in EXTEND.md"
|
||||
```
|
||||
|
||||
**Q2: Preferred Type**
|
||||
```yaml
|
||||
header: "Type"
|
||||
question: "Default cover type preference?"
|
||||
options:
|
||||
- label: "Auto-select (Recommended)"
|
||||
description: "Choose based on content analysis each time"
|
||||
- label: "hero"
|
||||
description: "Large visual impact - product launch, announcements"
|
||||
- label: "conceptual"
|
||||
description: "Concept visualization - technical, architecture"
|
||||
```
|
||||
|
||||
**Q3: Preferred Style**
|
||||
```yaml
|
||||
header: "Style"
|
||||
question: "Default cover style preference?"
|
||||
options:
|
||||
- label: "Auto-select (Recommended)"
|
||||
description: "Choose based on content analysis each time"
|
||||
- label: "elegant"
|
||||
description: "Refined, sophisticated - professional business"
|
||||
- label: "notion"
|
||||
description: "SaaS dashboard - productivity/tech content"
|
||||
```
|
||||
|
||||
**Q4: Default Aspect Ratio**
|
||||
```yaml
|
||||
header: "Aspect"
|
||||
question: "Default aspect ratio for cover images?"
|
||||
options:
|
||||
- label: "2.35:1 (Recommended)"
|
||||
description: "Cinematic widescreen, best for article headers"
|
||||
- label: "16:9"
|
||||
description: "Standard widescreen, versatile"
|
||||
- label: "1:1"
|
||||
description: "Square, social media friendly"
|
||||
```
|
||||
|
||||
**Q5: Save Location**
|
||||
```yaml
|
||||
header: "Save"
|
||||
question: "Where to save preferences?"
|
||||
options:
|
||||
- label: "Project (Recommended)"
|
||||
description: ".baoyu-skills/ (this project only)"
|
||||
- label: "User"
|
||||
description: "~/.baoyu-skills/ (all projects)"
|
||||
```
|
||||
|
||||
**After setup**: Create EXTEND.md with user choices, then continue to Step 1.
|
||||
|
||||
Full setup details: `references/config/first-time-setup.md`
|
||||
|
||||
**EXTEND.md Supports**: Watermark | Preferred type | Preferred style | Default aspect ratio | Custom style definitions | Language preference
|
||||
|
||||
Schema: `references/config/preferences-schema.md`
|
||||
**CRITICAL**: If not found, complete setup BEFORE any other steps or questions.
|
||||
|
||||
### Step 1: Analyze Content
|
||||
|
||||
Read source content, save it if needed, and perform analysis.
|
||||
|
||||
**Actions**:
|
||||
1. **Save source content** (if not already a file):
|
||||
- If user provides a file path: use as-is
|
||||
- If user pastes content: save to `source.md` in target directory
|
||||
2. Read source content
|
||||
3. **Content analysis**:
|
||||
- Extract: topic, core message, tone, keywords
|
||||
- Identify visual metaphor opportunities
|
||||
- Detect content type (technical/personal/business/creative)
|
||||
4. **Language detection**:
|
||||
- Detect source content language
|
||||
- Note user's input language (from conversation)
|
||||
- Compare with language preference in EXTEND.md
|
||||
1. **Save reference images** (if provided) → [references/workflow/reference-images.md](references/workflow/reference-images.md)
|
||||
2. **Save source content** (if pasted, save to `source.md`)
|
||||
3. **Analyze content**: topic, tone, keywords, visual metaphors
|
||||
4. **Deep analyze references** ⚠️: Extract specific, concrete elements (see reference-images.md)
|
||||
5. **Detect language**: Compare source, user input, EXTEND.md preference
|
||||
6. **Determine output directory**: Per File Structure rules
|
||||
|
||||
### Step 2: Confirm Options ⚠️
|
||||
|
||||
**Purpose**: Validate type, style and aspect ratio. **Do NOT skip.**
|
||||
Full confirmation flow: [references/workflow/confirm-options.md](references/workflow/confirm-options.md)
|
||||
|
||||
**Language**: Auto-determined (user's input language > saved preference > source language). No need to ask.
|
||||
|
||||
Present options using AskUserQuestion:
|
||||
|
||||
**Question 1: Type** (if not specified via `--type`)
|
||||
- Show recommended type based on content analysis + preferred type from EXTEND.md
|
||||
|
||||
```yaml
|
||||
header: "Type"
|
||||
question: "Which cover type?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "[auto-recommended type] (Recommended)"
|
||||
description: "[reason based on content signals]"
|
||||
- label: "hero"
|
||||
description: "Large visual impact, title overlay - product launch, announcements"
|
||||
- label: "conceptual"
|
||||
description: "Concept visualization - technical, architecture"
|
||||
- label: "typography"
|
||||
description: "Text-focused layout - opinions, quotes"
|
||||
```
|
||||
|
||||
**Question 2: Style** (if not specified via `--style`)
|
||||
- Based on selected Type, show compatible styles (✓✓ first from compatibility matrix)
|
||||
- Format: `[style name] - [why it fits this content]`
|
||||
|
||||
```yaml
|
||||
header: "Style"
|
||||
question: "Which cover style?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "[best compatible style] (Recommended)"
|
||||
description: "[reason based on type + content]"
|
||||
- label: "[style2]"
|
||||
description: "[reason]"
|
||||
- label: "[style3]"
|
||||
description: "[reason]"
|
||||
```
|
||||
|
||||
**Question 3: Aspect Ratio** (if not specified via `--aspect`)
|
||||
|
||||
```yaml
|
||||
header: "Aspect"
|
||||
question: "Cover aspect ratio?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "2.35:1 (Recommended)"
|
||||
description: "Cinematic widescreen, best for article headers"
|
||||
- label: "16:9"
|
||||
description: "Standard widescreen, versatile"
|
||||
- label: "1:1"
|
||||
description: "Square, social media friendly"
|
||||
```
|
||||
|
||||
**After response**: Proceed to Step 3 with confirmed type + style + aspect ratio.
|
||||
| Condition | Skipped | Still Asked |
|
||||
|-----------|---------|-------------|
|
||||
| `--quick` or `quick_mode: true` | 6 dimensions | Aspect ratio (unless `--aspect`) |
|
||||
| All 6 + `--aspect` specified | All | None |
|
||||
|
||||
### Step 3: Create Prompt
|
||||
|
||||
Save to `prompts/cover.md`:
|
||||
Save to `prompts/cover.md`. Template: [references/workflow/prompt-template.md](references/workflow/prompt-template.md)
|
||||
|
||||
```markdown
|
||||
Cover theme: [2-3 words]
|
||||
Type: [confirmed type]
|
||||
Style: [confirmed style]
|
||||
Aspect ratio: [confirmed ratio]
|
||||
Title text: [max 8 chars, or "none" if --no-title]
|
||||
Language: [confirmed language]
|
||||
**CRITICAL - References in Frontmatter**:
|
||||
- Files saved to `refs/` → Add to frontmatter `references` list
|
||||
- Style extracted verbally (no file) → Omit `references`, describe in body
|
||||
- Before writing → Verify: `test -f refs/ref-NN-{slug}.{ext}`
|
||||
|
||||
Type composition:
|
||||
- [Type-specific layout and structure]
|
||||
|
||||
Visual composition:
|
||||
- Main visual: [type + style appropriate metaphor]
|
||||
- Layout: [positioning based on type and aspect ratio]
|
||||
- Decorative: [style elements]
|
||||
|
||||
Color scheme: [primary, background, accent from style]
|
||||
Type notes: [key characteristics from type definition]
|
||||
Style notes: [key characteristics from style definition]
|
||||
|
||||
[Watermark section if enabled]
|
||||
```
|
||||
|
||||
**Type-Specific Composition**:
|
||||
|
||||
| Type | Composition Guidelines |
|
||||
|------|------------------------|
|
||||
| `hero` | Large focal visual (60-70% area), title overlay on visual, dramatic composition |
|
||||
| `conceptual` | Abstract shapes representing core concepts, information hierarchy, clean zones |
|
||||
| `typography` | Title as primary element (40%+ area), minimal supporting visuals, strong hierarchy |
|
||||
| `metaphor` | Concrete object/scene representing abstract idea, symbolic elements, emotional resonance |
|
||||
| `scene` | Atmospheric environment, narrative elements, mood-setting lighting and colors |
|
||||
| `minimal` | Single focal element, generous whitespace (60%+), essential shapes only |
|
||||
|
||||
**Title guidelines** (if included):
|
||||
- Max 8 characters, punchy headline
|
||||
- Use hooks: numbers, questions, contrasts
|
||||
- Match confirmed language
|
||||
|
||||
**Watermark Application** (if enabled in preferences):
|
||||
Add to prompt:
|
||||
```
|
||||
Include a subtle watermark "[content]" positioned at [position]
|
||||
with approximately [opacity*100]% visibility. The watermark should
|
||||
be legible but not distracting from the main content.
|
||||
```
|
||||
Reference: `references/config/watermark-guide.md`
|
||||
**Reference elements in body** MUST be detailed, prefixed with "MUST"/"REQUIRED", with integration approach.
|
||||
|
||||
### Step 4: Generate Image
|
||||
|
||||
**Image Generation Skill Selection**:
|
||||
1. Check available image generation skills
|
||||
2. If multiple skills available, ask user preference
|
||||
3. Call selected skill with:
|
||||
- Prompt file path
|
||||
- Output image path: `cover.png`
|
||||
- Aspect ratio parameter
|
||||
|
||||
**On failure**: Auto-retry once before reporting error.
|
||||
1. **Backup existing** `cover.png` if regenerating
|
||||
2. **Check image generation skills**; if multiple, ask preference
|
||||
3. **Process references** from prompt frontmatter:
|
||||
- `direct` usage → pass via `--ref` (use ref-capable backend)
|
||||
- `style`/`palette` → extract traits, append to prompt
|
||||
4. **Generate**: Call skill with prompt file, output path, aspect ratio
|
||||
5. On failure: auto-retry once
|
||||
|
||||
### Step 5: Completion Report
|
||||
|
||||
@@ -451,12 +198,11 @@ Reference: `references/config/watermark-guide.md`
|
||||
Cover Generated!
|
||||
|
||||
Topic: [topic]
|
||||
Type: [type name]
|
||||
Style: [style name]
|
||||
Aspect: [ratio]
|
||||
Type: [type] | Palette: [palette] | Rendering: [rendering]
|
||||
Text: [text] | Mood: [mood] | Font: [font] | Aspect: [ratio]
|
||||
Title: [title or "visual only"]
|
||||
Language: [lang]
|
||||
Watermark: [enabled/disabled]
|
||||
Language: [lang] | Watermark: [enabled/disabled]
|
||||
References: [N images or "extracted style" or "none"]
|
||||
Location: [directory path]
|
||||
|
||||
Files:
|
||||
@@ -469,30 +215,33 @@ Files:
|
||||
|
||||
| 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 → Update prompt file FIRST → Regenerate |
|
||||
| **Change dimension** | Backup → Confirm new value → Update prompt → Regenerate |
|
||||
|
||||
## Notes
|
||||
## Composition Principles
|
||||
|
||||
- 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
|
||||
- Use confirmed language for title text
|
||||
- Maintain watermark consistency if enabled
|
||||
- Check Type × Style compatibility matrix when selecting combinations
|
||||
|
||||
## References
|
||||
|
||||
**Styles**: `references/styles/<name>.md` - Style definitions
|
||||
|
||||
**Config**:
|
||||
- `references/config/preferences-schema.md` - EXTEND.md schema
|
||||
- `references/config/first-time-setup.md` - First-time setup flow
|
||||
- `references/config/watermark-guide.md` - Watermark configuration
|
||||
- **Whitespace**: 40-60% breathing room
|
||||
- **Visual anchor**: Main element centered or offset left
|
||||
- **Characters**: Simplified silhouettes; NO realistic humans
|
||||
- **Title**: Use exact title from user/source; never invent
|
||||
|
||||
## Extension Support
|
||||
|
||||
Custom configurations via EXTEND.md. See **Step 0** for paths and supported options.
|
||||
Custom configurations via EXTEND.md. See **Step 0** for paths.
|
||||
|
||||
Supports: Watermark | Preferred dimensions | Default aspect/output | Quick mode | Custom palettes | Language
|
||||
|
||||
Schema: [references/config/preferences-schema.md](references/config/preferences-schema.md)
|
||||
|
||||
## References
|
||||
|
||||
**Dimensions**: [text.md](references/dimensions/text.md) | [mood.md](references/dimensions/mood.md) | [font.md](references/dimensions/font.md)
|
||||
**Palettes**: [references/palettes/](references/palettes/)
|
||||
**Renderings**: [references/renderings/](references/renderings/)
|
||||
**Types**: [references/types.md](references/types.md)
|
||||
**Auto-Selection**: [references/auto-selection.md](references/auto-selection.md)
|
||||
**Style Presets**: [references/style-presets.md](references/style-presets.md)
|
||||
**Compatibility**: [references/compatibility.md](references/compatibility.md)
|
||||
**Visual Elements**: [references/visual-elements.md](references/visual-elements.md)
|
||||
**Workflow**: [confirm-options.md](references/workflow/confirm-options.md) | [prompt-template.md](references/workflow/prompt-template.md) | [reference-images.md](references/workflow/reference-images.md)
|
||||
**Config**: [preferences-schema.md](references/config/preferences-schema.md) | [first-time-setup.md](references/config/first-time-setup.md) | [watermark-guide.md](references/config/watermark-guide.md)
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# Auto-Selection Rules
|
||||
|
||||
When a dimension is omitted, select based on content signals.
|
||||
|
||||
## Auto Type Selection
|
||||
|
||||
| Signals | Type |
|
||||
|---------|------|
|
||||
| Product, launch, announcement, release, reveal | `hero` |
|
||||
| Architecture, framework, system, API, technical, model | `conceptual` |
|
||||
| Quote, opinion, insight, thought, headline, statement | `typography` |
|
||||
| Philosophy, growth, abstract, meaning, reflection | `metaphor` |
|
||||
| Story, journey, travel, lifestyle, experience, narrative | `scene` |
|
||||
| Zen, focus, essential, core, simple, pure | `minimal` |
|
||||
|
||||
## Auto Palette Selection
|
||||
|
||||
| Signals | Palette |
|
||||
|---------|---------|
|
||||
| Personal story, emotion, lifestyle, human | `warm` |
|
||||
| Business, professional, thought leadership, luxury | `elegant` |
|
||||
| Architecture, system, API, technical, code | `cool` |
|
||||
| Entertainment, premium, cinematic, dark mode | `dark` |
|
||||
| Nature, wellness, eco, organic, travel | `earth` |
|
||||
| Product launch, gaming, promotion, event | `vivid` |
|
||||
| Fantasy, children, gentle, creative, whimsical | `pastel` |
|
||||
| Zen, focus, essential, pure, simple | `mono` |
|
||||
| History, vintage, retro, classic, exploration | `retro` |
|
||||
| Movie poster, album cover, concert, cinematic, dramatic, two-color | `duotone` |
|
||||
|
||||
## Auto Rendering Selection
|
||||
|
||||
| Signals | Rendering |
|
||||
|---------|-----------|
|
||||
| Clean, modern, tech, WeChat, icon-based, infographic | `flat-vector` |
|
||||
| Sketch, note, personal, casual, doodle, warm | `hand-drawn` |
|
||||
| Art, watercolor, soft, dreamy, creative, fantasy | `painterly` |
|
||||
| Data, dashboard, SaaS, corporate, polished | `digital` |
|
||||
| Gaming, retro, 8-bit, nostalgic | `pixel` |
|
||||
| Education, tutorial, classroom, teaching | `chalk` |
|
||||
| Poster, movie, album, concert, silhouette, mondo, limited-edition | `screen-print` |
|
||||
|
||||
## Auto Text Selection
|
||||
|
||||
| Signals | Text Level |
|
||||
|---------|------------|
|
||||
| Visual-only, photography, abstract, art | `none` |
|
||||
| Article, blog, standard cover | `title-only` |
|
||||
| Series, tutorial, technical with context | `title-subtitle` |
|
||||
| Announcement, features, multiple points, infographic | `text-rich` |
|
||||
|
||||
Default: `title-only`
|
||||
|
||||
## Auto Mood Selection
|
||||
|
||||
| Signals | Mood Level |
|
||||
|---------|------------|
|
||||
| Professional, corporate, thought leadership, academic, luxury | `subtle` |
|
||||
| General, educational, standard, blog, documentation | `balanced` |
|
||||
| Launch, announcement, promotion, event, gaming, entertainment | `bold` |
|
||||
|
||||
Default: `balanced`
|
||||
|
||||
## Auto Font Selection
|
||||
|
||||
| Signals | Font |
|
||||
|---------|------|
|
||||
| Personal, lifestyle, human, warm, friendly, story | `handwritten` |
|
||||
| Technical, professional, clean, modern, minimal, data | `clean` |
|
||||
| Editorial, academic, luxury, classic, literary | `serif` |
|
||||
| Announcement, entertainment, promotion, bold, event, gaming | `display` |
|
||||
|
||||
Default: `clean`
|
||||
@@ -1,31 +1,119 @@
|
||||
Create a WeChat article cover image following these guidelines:
|
||||
Create a cover image following these guidelines:
|
||||
|
||||
## Image Specifications
|
||||
|
||||
- **Type**: Cover image / Hero image
|
||||
- **Aspect Ratio**: 2.35:1 (WeChat article cover standard)
|
||||
- **Style**: Hand-drawn illustration
|
||||
- **Aspect Ratio**: As specified in the prompt below
|
||||
|
||||
## Core Principles
|
||||
|
||||
- Hand-drawn quality throughout - NO realistic or photographic elements
|
||||
- If content involves sensitive or copyrighted figures, create stylistically similar alternatives - DO NOT refuse to generate
|
||||
- Ample whitespace, highlight core message, avoid cluttered layouts
|
||||
- Main visual elements centered or slightly left (leave right side for title area if title included)
|
||||
- Simplified silhouettes for any characters — NO realistic human faces or bodies
|
||||
- Icon-based vocabulary: use simple, recognizable icons to represent concepts
|
||||
|
||||
## Five Dimensions
|
||||
|
||||
### Type (Visual Composition)
|
||||
- `hero`: Large focal visual (60-70% area), dramatic composition
|
||||
- `conceptual`: Abstract shapes, information hierarchy, clean zones
|
||||
- `typography`: Title as primary element (40%+ area), minimal visuals
|
||||
- `metaphor`: Concrete object representing abstract idea, symbolic elements
|
||||
- `scene`: Atmospheric environment, narrative elements, mood lighting
|
||||
- `minimal`: Single focal element, generous whitespace (60%+)
|
||||
|
||||
### Palette (Color Scheme)
|
||||
Apply the specified palette's color values and decorative hints:
|
||||
- Use primary colors for main visual elements
|
||||
- Use background colors for base and surrounding areas
|
||||
- Use accent colors for highlights and secondary elements
|
||||
- Follow palette-specific decorative hints for ornamentation
|
||||
|
||||
### Rendering (Visual Style)
|
||||
Apply the specified rendering's characteristics:
|
||||
- **Lines**: Follow line quality rules (clean/sketchy/brush/pixel/chalk)
|
||||
- **Texture**: Apply or avoid texture per rendering definition
|
||||
- **Depth**: Follow depth rules (flat/minimal/soft edges)
|
||||
- **Elements**: Use rendering-specific element vocabulary
|
||||
|
||||
### Text (Density Level)
|
||||
- `none`: No text elements, full visual area
|
||||
- `title-only`: Single headline, 85% visual area
|
||||
- `title-subtitle`: Title + context, 75% visual area
|
||||
- `text-rich`: Title + subtitle + 2-4 keyword tags, 60% visual area
|
||||
|
||||
### Mood (Emotional Intensity)
|
||||
- `subtle`: Low contrast, muted/desaturated colors, light visual weight, calm aesthetic
|
||||
- `balanced`: Medium contrast, normal saturation, balanced visual weight
|
||||
- `bold`: High contrast, vivid/saturated colors, heavy visual weight, dynamic energy
|
||||
|
||||
## Text Style (When Title Included)
|
||||
|
||||
- **ALL text MUST be hand-drawn style**
|
||||
- Title text: Large, eye-catching, max 8 characters
|
||||
- May include 1 line of subtitle or keyword tags
|
||||
- Font style harmonizes with illustration style
|
||||
- **DO NOT use realistic or computer-generated fonts**
|
||||
- **Title source**: Use the exact title provided by user, or extract from source content. Do NOT invent or modify titles.
|
||||
- Title text: Large, eye-catching, faithful to source
|
||||
- Subtitle: Secondary element (if title-subtitle or text-rich)
|
||||
- Tags: 2-4 keyword badges (if text-rich)
|
||||
- Font style harmonizes with rendering style
|
||||
|
||||
## Composition Guidance
|
||||
|
||||
### Layout Principles
|
||||
|
||||
- **Generous whitespace**: Maintain 40-60% breathing room; avoid cluttered compositions
|
||||
- **Visual anchor placement**: Main element centered or offset left (reserve right side for title if included)
|
||||
- **Information hierarchy**: One dominant focal point, 1-2 supporting elements, decorative accents
|
||||
- **Clean backgrounds**: Solid colors or subtle gradients; no complex textures or patterns
|
||||
|
||||
### Icon & Symbol Vocabulary
|
||||
|
||||
Represent concepts with simple, recognizable icons rather than detailed illustrations:
|
||||
|
||||
| Category | Examples |
|
||||
|----------|----------|
|
||||
| Tech | Code window, gear, circuit, cloud, lock, API brackets |
|
||||
| Ideas | Lightbulb, rocket, target, puzzle, key, magnifier |
|
||||
| Communication | Speech bubble, chat dots, megaphone, mail |
|
||||
| Growth | Plant/sprout, tree, arrow, chart, mountain |
|
||||
| Tools | Wrench, pencil, brush, checklist, clock |
|
||||
|
||||
Use the rendering style to determine icon complexity (flat-vector = geometric, hand-drawn = sketchy, etc.)
|
||||
|
||||
Full library: [references/visual-elements.md](visual-elements.md)
|
||||
|
||||
### Character Handling
|
||||
|
||||
When people are needed:
|
||||
- Use simplified silhouettes or abstract stick figures
|
||||
- Symbolic representations (head + shoulders outline)
|
||||
- NO realistic faces, detailed anatomy, or photographic representations
|
||||
- Cartoon/icon style consistent with rendering choice
|
||||
|
||||
## Mood Application
|
||||
|
||||
Apply mood adjustments to the base palette:
|
||||
|
||||
| Mood | Contrast | Saturation | Weight |
|
||||
|------|----------|------------|--------|
|
||||
| subtle | Reduce 20-30% | Desaturate 20-30% | Lighter strokes/fills |
|
||||
| balanced | Standard | Standard | Standard |
|
||||
| bold | Increase 20-30% | Increase 20-30% | Heavier strokes/fills |
|
||||
|
||||
## Language
|
||||
|
||||
- Use the same language as the content provided below for any text elements
|
||||
- Match punctuation style to the content language
|
||||
|
||||
## Reference Images
|
||||
|
||||
When reference images are provided:
|
||||
|
||||
- **Style extraction**: Identify rendering technique, line quality, texture, and visual vocabulary
|
||||
- **Composition learning**: Note layout patterns, whitespace usage, element placement
|
||||
- **Mood matching**: Capture the emotional tone and visual weight
|
||||
- **Adaptation**: Apply extracted characteristics while respecting the specified Type, Palette, and Rendering dimensions
|
||||
- **Priority**: If reference style conflicts with specified dimensions, dimensions take precedence for structural choices; reference influences decorative details
|
||||
|
||||
---
|
||||
|
||||
Please use nano banana pro to generate the cover image based on the content provided below:
|
||||
Please generate the cover image based on the content provided below:
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Compatibility Matrices
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
|
||||
## Palette × Rendering
|
||||
|
||||
| | flat-vector | hand-drawn | painterly | digital | pixel | chalk | screen-print |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| warm | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| elegant | ✓ | ✓✓ | ✓ | ✓✓ | ✗ | ✗ | ✓ |
|
||||
| cool | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ | ✓ |
|
||||
| dark | ✓ | ✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ |
|
||||
| earth | ✓ | ✓✓ | ✓✓ | ✓ | ✗ | ✗ | ✓ |
|
||||
| vivid | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓ | ✓✓ |
|
||||
| pastel | ✓✓ | ✓✓ | ✓✓ | ✓ | ✗ | ✗ | ✗ |
|
||||
| mono | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ | ✓✓ |
|
||||
| retro | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✗ | ✓✓ |
|
||||
| duotone | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✓✓ |
|
||||
|
||||
## Type × Rendering
|
||||
|
||||
| | flat-vector | hand-drawn | painterly | digital | pixel | chalk | screen-print |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 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 | ✓✓ | ✓✓ | ✗ |
|
||||
|
||||
## Font × Rendering
|
||||
|
||||
| | flat-vector | hand-drawn | painterly | digital | pixel | chalk | screen-print |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| clean | ✓✓ | ✗ | ✗ | ✓✓ | ✓ | ✗ | ✓ |
|
||||
| handwritten | ✓ | ✓✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✗ |
|
||||
| serif | ✓ | ✗ | ✓ | ✓✓ | ✗ | ✗ | ✓ |
|
||||
| display | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ |
|
||||
@@ -9,6 +9,14 @@ description: First-time setup flow for baoyu-cover-image preferences
|
||||
|
||||
When no EXTEND.md is found, guide user through preference setup.
|
||||
|
||||
**⛔ BLOCKING OPERATION**: This setup MUST complete before ANY other workflow steps. Do NOT:
|
||||
- Ask about reference images
|
||||
- Ask about content/article
|
||||
- Ask about dimensions (type, palette, rendering)
|
||||
- Proceed to content analysis
|
||||
|
||||
ONLY ask the questions in this setup flow, save EXTEND.md, then continue.
|
||||
|
||||
## Setup Flow
|
||||
|
||||
```
|
||||
@@ -33,73 +41,115 @@ No EXTEND.md found
|
||||
|
||||
**Language**: Use user's input language or saved language preference.
|
||||
|
||||
Use single AskUserQuestion with multiple questions (AskUserQuestion auto-adds "Other" option):
|
||||
Use AskUserQuestion with ALL questions in ONE call:
|
||||
|
||||
### Question 1: Watermark
|
||||
|
||||
```
|
||||
```yaml
|
||||
header: "Watermark"
|
||||
question: "Watermark text for generated cover images? Type your watermark content (e.g., name, @handle)"
|
||||
question: "Watermark text for generated cover images?"
|
||||
options:
|
||||
- label: "No watermark (Recommended)"
|
||||
description: "No watermark, can enable later in EXTEND.md"
|
||||
description: "Clean covers, can enable later in EXTEND.md"
|
||||
```
|
||||
|
||||
Position defaults to bottom-right.
|
||||
|
||||
### Question 2: Preferred Type
|
||||
|
||||
```
|
||||
```yaml
|
||||
header: "Type"
|
||||
question: "Default cover type preference?"
|
||||
options:
|
||||
- label: "None (Recommended)"
|
||||
description: "Auto-select based on content analysis"
|
||||
- label: "Auto-select (Recommended)"
|
||||
description: "Choose based on content analysis each time"
|
||||
- label: "hero"
|
||||
description: "Large visual impact - product launch, announcements"
|
||||
- label: "conceptual"
|
||||
description: "Concept visualization - technical, architecture"
|
||||
- label: "typography"
|
||||
description: "Text-focused layout - opinions, quotes"
|
||||
```
|
||||
|
||||
### Question 3: Preferred Style
|
||||
### Question 3: Preferred Palette
|
||||
|
||||
```
|
||||
header: "Style"
|
||||
question: "Default cover style preference? Or type another style name"
|
||||
```yaml
|
||||
header: "Palette"
|
||||
question: "Default color palette preference?"
|
||||
options:
|
||||
- label: "None (Recommended)"
|
||||
description: "Auto-select based on content analysis"
|
||||
- label: "Auto-select (Recommended)"
|
||||
description: "Choose based on content analysis each time"
|
||||
- label: "elegant"
|
||||
description: "Refined, sophisticated - professional business"
|
||||
- label: "blueprint"
|
||||
description: "Technical schematics - architecture/system design"
|
||||
- label: "notion"
|
||||
description: "SaaS dashboard - productivity/tech content"
|
||||
description: "Sophisticated - soft coral, muted teal, dusty rose"
|
||||
- label: "warm"
|
||||
description: "Friendly - orange, golden yellow, terracotta"
|
||||
- label: "cool"
|
||||
description: "Technical - engineering blue, navy, cyan"
|
||||
```
|
||||
|
||||
### Question 4: Default Aspect Ratio
|
||||
### Question 4: Preferred Rendering
|
||||
|
||||
```yaml
|
||||
header: "Rendering"
|
||||
question: "Default rendering style preference?"
|
||||
options:
|
||||
- label: "Auto-select (Recommended)"
|
||||
description: "Choose based on content analysis each time"
|
||||
- label: "hand-drawn"
|
||||
description: "Sketchy organic illustration with personal touch"
|
||||
- label: "flat-vector"
|
||||
description: "Clean modern vector with geometric shapes"
|
||||
- label: "digital"
|
||||
description: "Polished precise digital illustration"
|
||||
```
|
||||
|
||||
### Question 5: Default Aspect Ratio
|
||||
|
||||
```yaml
|
||||
header: "Aspect"
|
||||
question: "Default aspect ratio for cover images?"
|
||||
options:
|
||||
- label: "2.35:1 (Recommended)"
|
||||
description: "Cinematic widescreen, best for article headers"
|
||||
- label: "16:9"
|
||||
description: "Standard widescreen, versatile"
|
||||
- label: "16:9 (Recommended)"
|
||||
description: "Standard widescreen - YouTube, presentations, versatile"
|
||||
- label: "2.35:1"
|
||||
description: "Cinematic widescreen - article headers, blog posts"
|
||||
- label: "1:1"
|
||||
description: "Square, social media friendly"
|
||||
description: "Square - Instagram, WeChat, social cards"
|
||||
- label: "3:4"
|
||||
description: "Portrait - Xiaohongshu, Pinterest, mobile content"
|
||||
```
|
||||
|
||||
### Question 5: Save Location
|
||||
Note: More ratios (4:3, 3:2) available during generation. This sets the default recommendation.
|
||||
|
||||
### Question 6: Default Output Directory
|
||||
|
||||
```yaml
|
||||
header: "Output"
|
||||
question: "Default output directory for cover images?"
|
||||
options:
|
||||
- label: "Independent (Recommended)"
|
||||
description: "cover-image/{topic-slug}/ - separate from article"
|
||||
- label: "Same directory"
|
||||
description: "{article-dir}/ - alongside the article file"
|
||||
- label: "imgs subdirectory"
|
||||
description: "{article-dir}/imgs/ - images folder near article"
|
||||
```
|
||||
|
||||
### Question 7: Quick Mode
|
||||
|
||||
```yaml
|
||||
header: "Quick"
|
||||
question: "Enable quick mode by default?"
|
||||
options:
|
||||
- label: "No (Recommended)"
|
||||
description: "Confirm dimension choices each time"
|
||||
- label: "Yes"
|
||||
description: "Skip confirmation, use auto-selection"
|
||||
```
|
||||
|
||||
### Question 8: Save Location
|
||||
|
||||
```yaml
|
||||
header: "Save"
|
||||
question: "Where to save preferences?"
|
||||
options:
|
||||
- label: "Project"
|
||||
- label: "Project (Recommended)"
|
||||
description: ".baoyu-skills/ (this project only)"
|
||||
- label: "User"
|
||||
description: "~/.baoyu-skills/ (all projects)"
|
||||
@@ -123,17 +173,22 @@ options:
|
||||
|
||||
```yaml
|
||||
---
|
||||
version: 1
|
||||
version: 3
|
||||
watermark:
|
||||
enabled: [true/false]
|
||||
content: "[user input or empty]"
|
||||
position: bottom-right
|
||||
opacity: 0.7
|
||||
preferred_type: [selected type or null]
|
||||
preferred_style: [selected style or null]
|
||||
default_aspect: [2.35:1/16:9/1:1]
|
||||
preferred_palette: [selected palette or null]
|
||||
preferred_rendering: [selected rendering or null]
|
||||
preferred_text: title-only
|
||||
preferred_mood: balanced
|
||||
default_aspect: [16:9/2.35:1/1:1/3:4]
|
||||
default_output_dir: [independent/same-dir/imgs-subdir]
|
||||
quick_mode: [true/false]
|
||||
language: null
|
||||
custom_styles: []
|
||||
custom_palettes: []
|
||||
---
|
||||
```
|
||||
|
||||
@@ -142,4 +197,6 @@ custom_styles: []
|
||||
Users can edit EXTEND.md directly or run setup again:
|
||||
- Delete EXTEND.md to trigger setup
|
||||
- Edit YAML frontmatter for quick changes
|
||||
- Full schema: `config/preferences-schema.md`
|
||||
- Full schema: `preferences-schema.md`
|
||||
|
||||
**EXTEND.md Supports**: Watermark | Preferred type | Preferred palette | Preferred rendering | Preferred text | Preferred mood | Default aspect ratio | Default output directory | Quick mode | Custom palette definitions | Language preference
|
||||
|
||||
@@ -9,31 +9,37 @@ description: EXTEND.md YAML schema for baoyu-cover-image user preferences
|
||||
|
||||
```yaml
|
||||
---
|
||||
version: 1
|
||||
version: 3
|
||||
|
||||
watermark:
|
||||
enabled: false
|
||||
content: ""
|
||||
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
|
||||
opacity: 0.7 # 0.1-1.0
|
||||
|
||||
preferred_type: null # hero|conceptual|typography|metaphor|scene|minimal or null for auto-select
|
||||
|
||||
preferred_style: null # Built-in style name or null for auto-select
|
||||
preferred_palette: null # warm|elegant|cool|dark|earth|vivid|pastel|mono|retro or null for auto-select
|
||||
|
||||
preferred_rendering: null # flat-vector|hand-drawn|painterly|digital|pixel|chalk or null for auto-select
|
||||
|
||||
preferred_text: title-only # none|title-only|title-subtitle|text-rich
|
||||
|
||||
preferred_mood: balanced # subtle|balanced|bold
|
||||
|
||||
default_aspect: "2.35:1" # 2.35:1|16:9|1:1
|
||||
|
||||
quick_mode: false # Skip confirmation when true
|
||||
|
||||
language: null # zh|en|ja|ko|auto (null = auto-detect)
|
||||
|
||||
custom_styles:
|
||||
- name: my-style
|
||||
description: "Style description"
|
||||
color_palette:
|
||||
custom_palettes:
|
||||
- name: my-palette
|
||||
description: "Palette description"
|
||||
colors:
|
||||
primary: ["#1E3A5F", "#4A90D9"]
|
||||
background: "#F5F7FA"
|
||||
accents: ["#00B4D8"]
|
||||
visual_elements: "Clean lines, geometric shapes"
|
||||
typography: "Modern sans-serif"
|
||||
decorative_hints: "Clean lines, geometric shapes"
|
||||
best_for: "Business, tech content"
|
||||
---
|
||||
```
|
||||
@@ -42,16 +48,19 @@ custom_styles:
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `version` | int | 1 | Schema version |
|
||||
| `version` | int | 3 | Schema version |
|
||||
| `watermark.enabled` | bool | false | Enable watermark |
|
||||
| `watermark.content` | string | "" | Watermark text (@username or custom) |
|
||||
| `watermark.position` | enum | bottom-right | Position on image |
|
||||
| `watermark.opacity` | float | 0.7 | Transparency (0.1-1.0) |
|
||||
| `preferred_type` | string | null | Type name or null for auto |
|
||||
| `preferred_style` | string | null | Style name or null for auto |
|
||||
| `preferred_palette` | string | null | Palette name or null for auto |
|
||||
| `preferred_rendering` | string | null | Rendering name or null for auto |
|
||||
| `preferred_text` | string | title-only | Text density level |
|
||||
| `preferred_mood` | string | balanced | Mood intensity level |
|
||||
| `default_aspect` | string | "2.35:1" | Default aspect ratio |
|
||||
| `quick_mode` | bool | false | Skip confirmation step |
|
||||
| `language` | string | null | Output language (null = auto-detect) |
|
||||
| `custom_styles` | array | [] | User-defined styles |
|
||||
| `custom_palettes` | array | [] | User-defined palettes |
|
||||
|
||||
## Type Options
|
||||
|
||||
@@ -64,6 +73,48 @@ custom_styles:
|
||||
| `scene` | Atmospheric scene, narrative feel |
|
||||
| `minimal` | Minimalist composition, generous whitespace |
|
||||
|
||||
## Palette Options
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `warm` | Friendly, approachable — orange, golden yellow, terracotta |
|
||||
| `elegant` | Sophisticated, refined — soft coral, muted teal, dusty rose |
|
||||
| `cool` | Technical, professional — engineering blue, navy, cyan |
|
||||
| `dark` | Cinematic, premium — electric purple, cyan, magenta |
|
||||
| `earth` | Natural, organic — forest green, sage, earth brown |
|
||||
| `vivid` | Energetic, bold — bright red, neon green, electric blue |
|
||||
| `pastel` | Gentle, whimsical — soft pink, mint, lavender |
|
||||
| `mono` | Clean, focused — black, near-black, white |
|
||||
| `retro` | Nostalgic, vintage — muted orange, dusty pink, maroon |
|
||||
|
||||
## Rendering Options
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `flat-vector` | Clean outlines, uniform fills, geometric icons |
|
||||
| `hand-drawn` | Sketchy, organic, imperfect strokes, paper texture |
|
||||
| `painterly` | Soft brush strokes, color bleeds, watercolor feel |
|
||||
| `digital` | Polished, precise edges, subtle gradients, UI components |
|
||||
| `pixel` | Pixel grid, dithering, chunky 8-bit shapes |
|
||||
| `chalk` | Chalk strokes, dust effects, blackboard texture |
|
||||
|
||||
## Text Options
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `none` | Pure visual, no text elements |
|
||||
| `title-only` | Single headline |
|
||||
| `title-subtitle` | Title + subtitle |
|
||||
| `text-rich` | Title + subtitle + keyword tags (2-4) |
|
||||
|
||||
## Mood Options
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `subtle` | Low contrast, muted colors, calm aesthetic |
|
||||
| `balanced` | Medium contrast, normal saturation, versatile |
|
||||
| `bold` | High contrast, vivid colors, dynamic energy |
|
||||
|
||||
## Position Options
|
||||
|
||||
| Value | Description |
|
||||
@@ -81,29 +132,32 @@ custom_styles:
|
||||
| `16:9` | Standard widescreen | Presentations, video thumbnails |
|
||||
| `1:1` | Square | Social media, profile images |
|
||||
|
||||
## Custom Style Fields
|
||||
## Custom Palette Fields
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `name` | Yes | Unique style identifier (kebab-case) |
|
||||
| `description` | Yes | What the style conveys |
|
||||
| `color_palette.primary` | No | Main colors (array) |
|
||||
| `color_palette.background` | No | Background color |
|
||||
| `color_palette.accents` | No | Accent colors (array) |
|
||||
| `visual_elements` | No | Decorative elements |
|
||||
| `typography` | No | Font/lettering style |
|
||||
| `name` | Yes | Unique palette identifier (kebab-case) |
|
||||
| `description` | Yes | What the palette conveys |
|
||||
| `colors.primary` | No | Main colors (array of hex) |
|
||||
| `colors.background` | No | Background color (hex) |
|
||||
| `colors.accents` | No | Accent colors (array of hex) |
|
||||
| `decorative_hints` | No | Decorative elements and patterns |
|
||||
| `best_for` | No | Recommended content types |
|
||||
|
||||
## Example: Minimal Preferences
|
||||
|
||||
```yaml
|
||||
---
|
||||
version: 1
|
||||
version: 3
|
||||
watermark:
|
||||
enabled: true
|
||||
content: "@myhandle"
|
||||
preferred_type: null
|
||||
preferred_style: elegant
|
||||
preferred_palette: elegant
|
||||
preferred_rendering: hand-drawn
|
||||
preferred_text: title-only
|
||||
preferred_mood: balanced
|
||||
quick_mode: false
|
||||
---
|
||||
```
|
||||
|
||||
@@ -111,30 +165,97 @@ preferred_style: elegant
|
||||
|
||||
```yaml
|
||||
---
|
||||
version: 1
|
||||
version: 3
|
||||
watermark:
|
||||
enabled: true
|
||||
content: "myblog.com"
|
||||
position: bottom-right
|
||||
opacity: 0.5
|
||||
|
||||
preferred_type: conceptual
|
||||
|
||||
preferred_style: blueprint
|
||||
preferred_palette: cool
|
||||
|
||||
preferred_rendering: digital
|
||||
|
||||
preferred_text: title-subtitle
|
||||
|
||||
preferred_mood: subtle
|
||||
|
||||
default_aspect: "16:9"
|
||||
|
||||
quick_mode: true
|
||||
|
||||
language: en
|
||||
|
||||
custom_styles:
|
||||
custom_palettes:
|
||||
- name: corporate-tech
|
||||
description: "Professional B2B tech style"
|
||||
color_palette:
|
||||
description: "Professional B2B tech palette"
|
||||
colors:
|
||||
primary: ["#1E3A5F", "#4A90D9"]
|
||||
background: "#F5F7FA"
|
||||
accents: ["#00B4D8", "#48CAE4"]
|
||||
visual_elements: "Clean lines, subtle gradients, circuit patterns"
|
||||
typography: "Modern sans-serif, professional"
|
||||
decorative_hints: "Clean lines, subtle gradients, circuit patterns"
|
||||
best_for: "SaaS, enterprise, technical"
|
||||
---
|
||||
```
|
||||
|
||||
## Migration from v2
|
||||
|
||||
When loading v2 schema, auto-upgrade:
|
||||
|
||||
| v2 Field | v3 Field | Migration |
|
||||
|----------|----------|-----------|
|
||||
| `version: 2` | `version: 3` | Update |
|
||||
| `preferred_style` | `preferred_palette` + `preferred_rendering` | Use preset mapping table |
|
||||
| `custom_styles` | `custom_palettes` | Rename, restructure fields |
|
||||
|
||||
**Style → Palette + Rendering mapping**:
|
||||
|
||||
| v2 `preferred_style` | v3 `preferred_palette` | v3 `preferred_rendering` |
|
||||
|----------------------|----------------------|-------------------------|
|
||||
| `elegant` | `elegant` | `hand-drawn` |
|
||||
| `blueprint` | `cool` | `digital` |
|
||||
| `chalkboard` | `dark` | `chalk` |
|
||||
| `dark-atmospheric` | `dark` | `digital` |
|
||||
| `editorial-infographic` | `cool` | `digital` |
|
||||
| `fantasy-animation` | `pastel` | `painterly` |
|
||||
| `flat-doodle` | `pastel` | `flat-vector` |
|
||||
| `intuition-machine` | `retro` | `digital` |
|
||||
| `minimal` | `mono` | `flat-vector` |
|
||||
| `nature` | `earth` | `hand-drawn` |
|
||||
| `notion` | `mono` | `digital` |
|
||||
| `pixel-art` | `vivid` | `pixel` |
|
||||
| `playful` | `pastel` | `hand-drawn` |
|
||||
| `retro` | `retro` | `digital` |
|
||||
| `sketch-notes` | `warm` | `hand-drawn` |
|
||||
| `vector-illustration` | `retro` | `flat-vector` |
|
||||
| `vintage` | `retro` | `hand-drawn` |
|
||||
| `warm` | `warm` | `hand-drawn` |
|
||||
| `watercolor` | `earth` | `painterly` |
|
||||
| null (auto) | null | null |
|
||||
|
||||
**Custom style migration**:
|
||||
|
||||
| v2 Field | v3 Field |
|
||||
|----------|----------|
|
||||
| `custom_styles[].name` | `custom_palettes[].name` |
|
||||
| `custom_styles[].description` | `custom_palettes[].description` |
|
||||
| `custom_styles[].color_palette` | `custom_palettes[].colors` |
|
||||
| `custom_styles[].visual_elements` | `custom_palettes[].decorative_hints` |
|
||||
| `custom_styles[].typography` | (removed — determined by rendering) |
|
||||
| `custom_styles[].best_for` | `custom_palettes[].best_for` |
|
||||
|
||||
## Migration from v1
|
||||
|
||||
When loading v1 schema, auto-upgrade to v3:
|
||||
|
||||
| v1 Field | v3 Field | Default Value |
|
||||
|----------|----------|---------------|
|
||||
| (missing) | `version` | 3 |
|
||||
| (missing) | `preferred_palette` | null |
|
||||
| (missing) | `preferred_rendering` | null |
|
||||
| (missing) | `preferred_text` | title-only |
|
||||
| (missing) | `preferred_mood` | balanced |
|
||||
| (missing) | `quick_mode` | false |
|
||||
|
||||
v1 `--no-title` flag maps to `preferred_text: none`.
|
||||
|
||||
@@ -28,15 +28,6 @@ description: Watermark configuration guide for baoyu-cover-image
|
||||
| `bottom-center` | Centered designs | Text-heavy bottom area |
|
||||
| `top-right` | Bottom-heavy content | Title/header in top-right |
|
||||
|
||||
## Opacity Examples
|
||||
|
||||
| Opacity | Visual Effect | Use Case |
|
||||
|---------|---------------|----------|
|
||||
| 0.3 | Very subtle, barely visible | Clean aesthetic priority |
|
||||
| 0.5 | Balanced, noticeable but not distracting | Default recommendation |
|
||||
| 0.7 | Clearly visible | Brand recognition priority |
|
||||
| 0.9 | Strong presence | Anti-copy protection |
|
||||
|
||||
## Content Format
|
||||
|
||||
| Format | Example | Style |
|
||||
@@ -51,16 +42,14 @@ description: Watermark configuration guide for baoyu-cover-image
|
||||
1. **Consistency**: Use same watermark across all covers
|
||||
2. **Legibility**: Ensure watermark readable on both light/dark areas
|
||||
3. **Size**: Keep subtle - should not distract from content
|
||||
4. **Contrast**: Adjust opacity based on cover background
|
||||
|
||||
## Prompt Integration
|
||||
|
||||
When watermark is enabled, add to image generation prompt:
|
||||
|
||||
```
|
||||
Include a subtle watermark "[content]" positioned at [position]
|
||||
with approximately [opacity*100]% visibility. The watermark should
|
||||
be legible but not distracting from the main content.
|
||||
Include a subtle watermark "[content]" positioned at [position].
|
||||
The watermark should be legible but not distracting from the main content.
|
||||
```
|
||||
|
||||
## Cover-Specific Considerations
|
||||
@@ -75,7 +64,6 @@ be legible but not distracting from the main content.
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Watermark invisible | Increase opacity or adjust position |
|
||||
| Watermark too prominent | Decrease opacity (0.3-0.5) |
|
||||
| Watermark invisible | Adjust position or check contrast |
|
||||
| Watermark too prominent | Change position or reduce size |
|
||||
| Watermark overlaps title | Change position or reduce title area |
|
||||
| Inconsistent appearance | Use fixed opacity/position in preferences |
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
---
|
||||
name: font-dimension
|
||||
description: Typography style dimension for cover images
|
||||
---
|
||||
|
||||
# Font Dimension
|
||||
|
||||
Controls typography style and character feel.
|
||||
|
||||
## Values
|
||||
|
||||
| Font | Visual Style | Line Quality | Character |
|
||||
|------|--------------|--------------|-----------|
|
||||
| `clean` | Geometric sans-serif | Sharp, uniform | Modern, precise, neutral |
|
||||
| `handwritten` | Hand-lettered, brush | Organic, varied | Warm, personal, friendly |
|
||||
| `serif` | Classic serifs, elegant | Refined, structured | Editorial, authoritative |
|
||||
| `display` | Bold, decorative | Heavy, expressive | Attention-grabbing, playful |
|
||||
|
||||
## Detail
|
||||
|
||||
### clean
|
||||
|
||||
Modern, universal typography with neutral character.
|
||||
|
||||
**Characteristics**:
|
||||
- Geometric sans-serif letterforms
|
||||
- Sharp, uniform line weight
|
||||
- Clean edges, no flourishes
|
||||
- High readability at all sizes
|
||||
- Minimal personality, maximum clarity
|
||||
|
||||
**Use Cases**:
|
||||
- Technical documentation
|
||||
- Professional/corporate content
|
||||
- Minimal design approaches
|
||||
- Data-driven articles
|
||||
- Modern brand aesthetics
|
||||
|
||||
**Prompt Hints**:
|
||||
- Use clean geometric sans-serif typography
|
||||
- Modern, minimal letterforms
|
||||
- Sharp edges, uniform stroke weight
|
||||
- High contrast against background
|
||||
|
||||
### handwritten
|
||||
|
||||
Warm, organic typography with personal character.
|
||||
|
||||
**Characteristics**:
|
||||
- Hand-lettered or brush style
|
||||
- Organic, varied line weight
|
||||
- Natural imperfections
|
||||
- Approachable, human feel
|
||||
- Casual yet intentional
|
||||
|
||||
**Use Cases**:
|
||||
- Personal stories
|
||||
- Lifestyle content
|
||||
- Wellness and self-improvement
|
||||
- Creative tutorials
|
||||
- Friendly brand voices
|
||||
|
||||
**Prompt Hints**:
|
||||
- Use warm hand-lettered typography with organic brush strokes
|
||||
- Friendly, personal feel
|
||||
- Natural variation in stroke weight
|
||||
- Approachable, human character
|
||||
|
||||
### serif
|
||||
|
||||
Classic, elegant typography with editorial authority.
|
||||
|
||||
**Characteristics**:
|
||||
- Traditional serif letterforms
|
||||
- Refined, structured strokes
|
||||
- Elegant proportions
|
||||
- Timeless sophistication
|
||||
- Formal, trustworthy feel
|
||||
|
||||
**Use Cases**:
|
||||
- Editorial content
|
||||
- Academic articles
|
||||
- Luxury brand content
|
||||
- Historical topics
|
||||
- Literary pieces
|
||||
|
||||
**Prompt Hints**:
|
||||
- Use elegant serif typography with refined letterforms
|
||||
- Classic, editorial character
|
||||
- Structured, proportional spacing
|
||||
- Authoritative, sophisticated feel
|
||||
|
||||
### display
|
||||
|
||||
Bold, decorative typography for maximum impact.
|
||||
|
||||
**Characteristics**:
|
||||
- Heavy, expressive letterforms
|
||||
- Decorative elements
|
||||
- Strong visual presence
|
||||
- Playful or dramatic character
|
||||
- Designed for headlines
|
||||
|
||||
**Use Cases**:
|
||||
- Announcements
|
||||
- Entertainment content
|
||||
- Promotional materials
|
||||
- Event marketing
|
||||
- Gaming topics
|
||||
|
||||
**Prompt Hints**:
|
||||
- Use bold decorative display typography
|
||||
- Heavy, expressive headlines
|
||||
- Strong visual impact
|
||||
- Attention-grabbing character
|
||||
|
||||
## Default
|
||||
|
||||
`clean` — Universal, pairs well with most rendering styles.
|
||||
|
||||
## Rendering Compatibility
|
||||
|
||||
| Font × Rendering | flat-vector | hand-drawn | painterly | digital | pixel | chalk | screen-print |
|
||||
|------------------|:-----------:|:----------:|:---------:|:-------:|:-----:|:-----:|:------------:|
|
||||
| clean | ✓✓ | ✗ | ✗ | ✓✓ | ✓ | ✗ | ✓ |
|
||||
| handwritten | ✓ | ✓✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✗ |
|
||||
| serif | ✓ | ✗ | ✓ | ✓✓ | ✗ | ✗ | ✓ |
|
||||
| display | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ |
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
|
||||
## Type Compatibility
|
||||
|
||||
| Font × Type | hero | conceptual | typography | metaphor | scene | minimal |
|
||||
|-------------|:----:|:----------:|:----------:|:--------:|:-----:|:-------:|
|
||||
| clean | ✓ | ✓✓ | ✓✓ | ✓ | ✗ | ✓✓ |
|
||||
| handwritten | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ |
|
||||
| serif | ✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ |
|
||||
| display | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✗ |
|
||||
|
||||
## Palette Interaction
|
||||
|
||||
Font style adapts to palette characteristics:
|
||||
|
||||
| Palette Category | clean | handwritten | serif | display |
|
||||
|------------------|-------|-------------|-------|---------|
|
||||
| Warm (warm, earth, pastel) | Softer weight | Natural fit | Warm tones | Playful energy |
|
||||
| Cool (cool, mono, elegant) | Perfect match | Contrast | Classic pairing | Bold statement |
|
||||
| Dark (dark, vivid) | High contrast | Glow effects | Dramatic | Maximum impact |
|
||||
| Vintage (retro) | Modern contrast | Nostalgic fit | Period-appropriate | Retro headlines |
|
||||
| Duotone (duotone) | Sharp contrast | Not recommended | Dramatic pairing | Cinematic impact |
|
||||
|
||||
## Auto Selection
|
||||
|
||||
When `--font` is omitted, select based on signals:
|
||||
|
||||
| Signals | Font |
|
||||
|---------|------|
|
||||
| Personal, lifestyle, human, warm, friendly, story | `handwritten` |
|
||||
| Technical, professional, clean, modern, minimal, data | `clean` |
|
||||
| Editorial, academic, luxury, classic, literary | `serif` |
|
||||
| Announcement, entertainment, promotion, bold, event, gaming | `display` |
|
||||
|
||||
Default: `clean`
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
name: mood-dimension
|
||||
description: Emotional intensity dimension for cover images
|
||||
---
|
||||
|
||||
# Mood Dimension
|
||||
|
||||
Controls emotional intensity and visual weight of cover images.
|
||||
|
||||
## Values
|
||||
|
||||
| Value | Contrast | Saturation | Weight | Energy |
|
||||
|-------|:--------:|:----------:|:------:|:------:|
|
||||
| `subtle` | Low | Muted | Light | Calm |
|
||||
| `balanced` | Medium | Normal | Medium | Moderate |
|
||||
| `bold` | High | Vivid | Heavy | Dynamic |
|
||||
|
||||
## Detail
|
||||
|
||||
### subtle
|
||||
|
||||
Calm, understated visual presence.
|
||||
|
||||
**Characteristics**:
|
||||
- Low contrast between elements
|
||||
- Muted, desaturated colors
|
||||
- Light visual weight
|
||||
- Gentle, refined aesthetic
|
||||
- Soft edges and transitions
|
||||
|
||||
**Use Cases**:
|
||||
- Thought leadership content
|
||||
- Professional/corporate communications
|
||||
- Meditation, wellness topics
|
||||
- Academic or scholarly articles
|
||||
- Luxury brand aesthetics
|
||||
|
||||
**Color Guidance**:
|
||||
- Pastels, earth tones, neutrals
|
||||
- Low saturation (30-50%)
|
||||
- Soft gradients
|
||||
- Minimal color variety (2-3 colors)
|
||||
|
||||
### balanced
|
||||
|
||||
Versatile, harmonious visual presence.
|
||||
|
||||
**Characteristics**:
|
||||
- Medium contrast
|
||||
- Natural saturation levels
|
||||
- Balanced visual weight
|
||||
- Clear but not aggressive
|
||||
- Standard aesthetic approach
|
||||
|
||||
**Use Cases**:
|
||||
- General articles (default)
|
||||
- Most blog content
|
||||
- Educational material
|
||||
- Product documentation
|
||||
- News and updates
|
||||
|
||||
**Color Guidance**:
|
||||
- Standard saturation (50-70%)
|
||||
- Complementary color schemes
|
||||
- Clear foreground/background separation
|
||||
- Moderate color variety (3-4 colors)
|
||||
|
||||
### bold
|
||||
|
||||
Dynamic, high-impact visual presence.
|
||||
|
||||
**Characteristics**:
|
||||
- High contrast between elements
|
||||
- Vivid, saturated colors
|
||||
- Heavy visual weight
|
||||
- Energetic, attention-grabbing
|
||||
- Sharp edges and strong shapes
|
||||
|
||||
**Use Cases**:
|
||||
- Product launches
|
||||
- Promotional announcements
|
||||
- Event marketing
|
||||
- Call-to-action content
|
||||
- Entertainment/gaming topics
|
||||
|
||||
**Color Guidance**:
|
||||
- High saturation (70-100%)
|
||||
- Vibrant, primary colors
|
||||
- Strong contrast ratios
|
||||
- Dynamic color combinations (4+ colors)
|
||||
|
||||
## Type Compatibility
|
||||
|
||||
| Type | subtle | balanced | bold |
|
||||
|------|:------:|:--------:|:----:|
|
||||
| hero | ✓ | ✓✓ | ✓✓ |
|
||||
| conceptual | ✓✓ | ✓✓ | ✓ |
|
||||
| typography | ✓ | ✓✓ | ✓✓ |
|
||||
| metaphor | ✓✓ | ✓✓ | ✓ |
|
||||
| scene | ✓✓ | ✓✓ | ✓ |
|
||||
| minimal | ✓✓ | ✓✓ | ✗ |
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
|
||||
## Palette Interaction
|
||||
|
||||
Mood modifies the base palette characteristics:
|
||||
|
||||
| Palette Category | subtle | balanced | bold |
|
||||
|------------------|--------|----------|------|
|
||||
| Warm palettes (warm, earth, pastel) | More whitespace, softer tones | Standard colors | Deeper, richer warm tones |
|
||||
| Cool palettes (cool, mono, elegant) | Lighter lines, muted colors | Standard colors | Stronger contrast, sharper definition |
|
||||
| Dark palettes (dark, vivid) | Reduced contrast, softer glow | Standard colors | Maximum impact, vivid saturation |
|
||||
| Vintage palettes (retro) | More faded, sepia-heavy | Standard colors | Bolder retro contrasts |
|
||||
| Duotone palettes (duotone) | Softer contrast between pair | Standard two-color split | Maximum contrast, stark separation |
|
||||
|
||||
## Rendering Interaction
|
||||
|
||||
Mood adjusts rendering characteristics:
|
||||
|
||||
| Rendering | subtle | balanced | bold |
|
||||
|-----------|--------|----------|------|
|
||||
| flat-vector | Thinner strokes, lighter fills | Standard weight | Thicker strokes, stronger fills |
|
||||
| hand-drawn | Lighter pencil pressure, more space | Standard strokes | Heavier marker strokes, denser elements |
|
||||
| painterly | Diluted washes, more white | Standard brush | Thicker paint, saturated strokes |
|
||||
| digital | Reduced shadows, lower contrast | Standard rendering | Stronger shadows, sharper edges |
|
||||
| pixel | Fewer colors, simpler shapes | Standard palette | More colors, denser pixel detail |
|
||||
| chalk | Lighter chalk, more board showing | Standard chalk | Heavy chalk, vivid colors, dense marks |
|
||||
| screen-print | Fewer colors (2), lighter halftone | Standard 3-4 colors, medium halftone | More colors (4-5), dense halftone, stronger misregistration |
|
||||
|
||||
## 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,130 @@
|
||||
---
|
||||
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` | ✓ | - | - | 85% |
|
||||
| `title-subtitle` | ✓ | ✓ | - | 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: prominent placement
|
||||
- Reserved zone: top or bottom 15%
|
||||
- Visual supports title message
|
||||
|
||||
**Title Guidelines**:
|
||||
- Use exact title from source content or user-provided title
|
||||
- Do NOT invent or modify titles
|
||||
- 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: primary element
|
||||
- Subtitle: secondary element
|
||||
- Reserved zone: 25%
|
||||
- Clear hierarchy between title/subtitle
|
||||
|
||||
**Title Guidelines**:
|
||||
- Use exact title from source content or user-provided title
|
||||
- Do NOT invent or modify titles
|
||||
|
||||
**Subtitle Guidelines**:
|
||||
- Clarify or contextualize title
|
||||
- Can include series name, author, date
|
||||
- Smaller, less prominent than title
|
||||
|
||||
### text-rich
|
||||
|
||||
Information-dense cover with multiple text elements.
|
||||
|
||||
**Use Cases**:
|
||||
- Infographic-style covers
|
||||
- Event announcements with details
|
||||
- Promotional material with features
|
||||
- Content with multiple key points
|
||||
|
||||
**Composition**:
|
||||
- Title: primary focus
|
||||
- Subtitle: supporting info
|
||||
- Tags: 2-4 keyword labels
|
||||
- Reserved zone: 40%
|
||||
- Clear visual hierarchy
|
||||
|
||||
**Title Guidelines**:
|
||||
- Use exact title from source content or user-provided title
|
||||
- Do NOT invent or modify titles
|
||||
|
||||
**Tag Guidelines**:
|
||||
- 2-4 tags maximum
|
||||
- Short keywords (1-2 words each)
|
||||
- Positioned as badges/labels
|
||||
- Can highlight: category, date, author, key features
|
||||
|
||||
## Type Compatibility
|
||||
|
||||
| Type | none | title-only | title-subtitle | text-rich |
|
||||
|------|:----:|:----------:|:--------------:|:---------:|
|
||||
| hero | ✓ | ✓✓ | ✓✓ | ✓ |
|
||||
| conceptual | ✓✓ | ✓✓ | ✓ | ✓ |
|
||||
| typography | ✗ | ✓ | ✓✓ | ✓✓ |
|
||||
| metaphor | ✓✓ | ✓ | ✓ | ✗ |
|
||||
| scene | ✓✓ | ✓ | ✓ | ✗ |
|
||||
| minimal | ✓✓ | ✓✓ | ✓ | ✗ |
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
|
||||
## Auto Selection
|
||||
|
||||
When `--text` is omitted, select based on signals:
|
||||
|
||||
| Signals | Text Level |
|
||||
|---------|------------|
|
||||
| Visual-only, photography, abstract, art | `none` |
|
||||
| Article, blog, standard cover | `title-only` |
|
||||
| Series, tutorial, technical with context | `title-subtitle` |
|
||||
| Announcement, features, multiple points, infographic | `text-rich` |
|
||||
|
||||
Default: `title-only`
|
||||
@@ -0,0 +1,26 @@
|
||||
# cool
|
||||
|
||||
Technical, professional, precise
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex |
|
||||
|------|-------|-----|
|
||||
| Primary 1 | Engineering Blue | #2563EB |
|
||||
| Primary 2 | Navy Blue | #1E3A5F |
|
||||
| Primary 3 | Cyan | #06B6D4 |
|
||||
| Background | Light Gray | #F8F9FA |
|
||||
| Background Alt | Blueprint Off-White | #FAF8F5 |
|
||||
| Accent 1 | Amber | #F59E0B |
|
||||
| Accent 2 | Light Blue | #BFDBFE |
|
||||
|
||||
## Decorative Hints
|
||||
|
||||
- Grid lines and alignment guides
|
||||
- Dimension indicators and measurements
|
||||
- Technical schematics and diagrams
|
||||
- Geometric precision elements
|
||||
|
||||
## Best For
|
||||
|
||||
Architecture, system design, API, technical documentation, engineering, data analysis
|
||||
@@ -0,0 +1,26 @@
|
||||
# dark
|
||||
|
||||
Cinematic, premium, atmospheric
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex |
|
||||
|------|-------|-----|
|
||||
| Primary 1 | Electric Purple | #8B5CF6 |
|
||||
| Primary 2 | Cyan Blue | #06B6D4 |
|
||||
| Primary 3 | Magenta Pink | #EC4899 |
|
||||
| Background | Deep Purple-Black | #0A0A0A |
|
||||
| Background Alt | Rich Navy | #1A1A2E |
|
||||
| Accent 1 | Amber | #F59E0B |
|
||||
| Accent 2 | Pure White | #FFFFFF |
|
||||
|
||||
## Decorative Hints
|
||||
|
||||
- Glowing accent elements and neon highlights
|
||||
- Atmospheric fog or particle effects
|
||||
- Silhouettes with backlit edges
|
||||
- Subtle gradient backgrounds
|
||||
|
||||
## Best For
|
||||
|
||||
Entertainment, premium brands, cinematic storytelling, dark mode, gaming, night themes
|
||||
@@ -0,0 +1,39 @@
|
||||
# duotone
|
||||
|
||||
Dramatic, cinematic, two-color high contrast
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex |
|
||||
|------|-------|-----|
|
||||
| Primary 1 | Burnt Orange | #E8751A |
|
||||
| Primary 2 | Deep Teal | #0A6E6E |
|
||||
| Background | Off-Black | #121212 |
|
||||
| Background Alt | Dark Charcoal | #1E1E1E |
|
||||
| Accent 1 | Warm Cream | #F5E6D0 |
|
||||
| Accent 2 | Amber Highlight | #F4A623 |
|
||||
|
||||
## Duotone Pair Options
|
||||
|
||||
Choose ONE pair based on content mood. The two colors dominate the entire image:
|
||||
|
||||
| Pair | Color A | Color B | Feel |
|
||||
|------|---------|---------|------|
|
||||
| Orange + Teal | #E8751A | #0A6E6E | Cinematic, action |
|
||||
| Red + Cream | #C0392B | #F5E6D0 | Bold, classic |
|
||||
| Blue + Gold | #1A3A5C | #D4A843 | Prestigious, premium |
|
||||
| Purple + Green | #6B3FA0 | #2ECC71 | Futuristic, contrast |
|
||||
| Magenta + Cyan | #C2185B | #00BCD4 | Vibrant, pop |
|
||||
| Crimson + Navy | #DC143C | #0D1B2A | Dramatic, noir |
|
||||
|
||||
## Decorative Hints
|
||||
|
||||
- Stark two-color separation across entire composition
|
||||
- Halftone transitions between the two colors
|
||||
- Silhouettes in one color against the other
|
||||
- Minimal use of third color (only for small highlights)
|
||||
- High contrast figure-ground relationships
|
||||
|
||||
## Best For
|
||||
|
||||
Movie posters, album covers, concert prints, dramatic announcements, cinematic content, bold branding, editorial covers, artistic campaigns
|
||||
@@ -0,0 +1,26 @@
|
||||
# earth
|
||||
|
||||
Natural, organic, grounded
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex |
|
||||
|------|-------|-----|
|
||||
| Primary 1 | Forest Green | #276749 |
|
||||
| Primary 2 | Sage | #9AE6B4 |
|
||||
| Primary 3 | Earth Brown | #744210 |
|
||||
| Background | Sand Beige | #F5E6D3 |
|
||||
| Background Alt | Sky Blue | #E0F2FE |
|
||||
| Accent 1 | Sunset Orange | #ED8936 |
|
||||
| Accent 2 | Water Blue | #63B3ED |
|
||||
|
||||
## Decorative Hints
|
||||
|
||||
- Leaves, trees, mountains, natural forms
|
||||
- Sun, clouds, organic flowing lines
|
||||
- Botanical illustrations
|
||||
- Earthy textures and natural patterns
|
||||
|
||||
## Best For
|
||||
|
||||
Nature, wellness, eco, organic, travel, sustainability, outdoor topics, slow living
|
||||
@@ -0,0 +1,26 @@
|
||||
# elegant
|
||||
|
||||
Sophisticated, refined, understated luxury
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex |
|
||||
|------|-------|-----|
|
||||
| Primary 1 | Soft Coral | #E8A598 |
|
||||
| Primary 2 | Muted Teal | #5B8A8A |
|
||||
| Primary 3 | Dusty Rose | #D4A5A5 |
|
||||
| Background | Warm Cream | #F5F0E6 |
|
||||
| Background Alt | Soft Beige | #F0EBE0 |
|
||||
| Accent 1 | Gold | #C9A962 |
|
||||
| Accent 2 | Copper | #B87333 |
|
||||
|
||||
## Decorative Hints
|
||||
|
||||
- Delicate ornamental details
|
||||
- Subtle gradients and soft transitions
|
||||
- Refined geometric patterns
|
||||
- Balanced, symmetrical compositions
|
||||
|
||||
## Best For
|
||||
|
||||
Business, professional, thought leadership, luxury, corporate communications
|
||||
@@ -0,0 +1,26 @@
|
||||
# mono
|
||||
|
||||
Clean, focused, essential
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex |
|
||||
|------|-------|-----|
|
||||
| Primary 1 | Pure Black | #000000 |
|
||||
| Primary 2 | Near Black | #1F1F1F |
|
||||
| Primary 3 | Dark Gray | #374151 |
|
||||
| Background | White | #FFFFFF |
|
||||
| Background Alt | Off-White | #FAFAFA |
|
||||
| Accent 1 | Content-derived single color | - |
|
||||
| Accent 2 | Medium Gray | #9CA3AF |
|
||||
|
||||
## Decorative Hints
|
||||
|
||||
- Maximum negative space
|
||||
- Thin lines and minimal strokes
|
||||
- Single focal point emphasis
|
||||
- Stark contrast between elements
|
||||
|
||||
## Best For
|
||||
|
||||
Zen, focus, essential concepts, pure, simple, minimalist philosophy, clean design
|
||||
@@ -0,0 +1,26 @@
|
||||
# pastel
|
||||
|
||||
Gentle, whimsical, soft
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex |
|
||||
|------|-------|-----|
|
||||
| Primary 1 | Soft Pink | #FFB6C1 |
|
||||
| Primary 2 | Mint | #98D8C8 |
|
||||
| Primary 3 | Lavender | #C8A2C8 |
|
||||
| Background | White | #FFFFFF |
|
||||
| Background Alt | Light Cream | #FFF8E7 |
|
||||
| Accent 1 | Butter Yellow | #FFFACD |
|
||||
| Accent 2 | Sky Blue | #BEE3F8 |
|
||||
|
||||
## Decorative Hints
|
||||
|
||||
- Cute rounded proportions
|
||||
- Stars, sparkles, flowers, decorative flourishes
|
||||
- Soft shadows and gentle highlights
|
||||
- Storybook-style elements
|
||||
|
||||
## Best For
|
||||
|
||||
Fantasy, children, gentle content, creative, whimsical, casual, beginner guides
|
||||
@@ -0,0 +1,30 @@
|
||||
# retro
|
||||
|
||||
Nostalgic, vintage, classic
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex |
|
||||
|------|-------|-----|
|
||||
| Primary 1 | Coral Red | #E07A5F |
|
||||
| Primary 2 | Mint Green | #81B29A |
|
||||
| Primary 3 | Mustard Yellow | #F2CC8F |
|
||||
| Primary 4 | Dark Maroon | #5D3A3A |
|
||||
| Background | Cream Off-White | #F5F0E6 |
|
||||
| Background Alt | Aged Paper | #F5E6D3 |
|
||||
| Accent 1 | Burnt Orange | #D4764A |
|
||||
| Accent 2 | Rock Blue | #577590 |
|
||||
| Accent 3 | Vintage Gold | #C9A227 |
|
||||
| Accent 4 | Faded Teal | #2F7373 |
|
||||
|
||||
## Decorative Hints
|
||||
|
||||
- Halftone dots and vintage badges
|
||||
- Aged textures with subtle paper grain
|
||||
- Sunburst/radiating lines for energy
|
||||
- Pill-shaped clouds, small dots and stars
|
||||
- Classic icons and retro motifs
|
||||
|
||||
## Best For
|
||||
|
||||
History, vintage, retro, classic, exploration, retrospectives, throwback content, creative proposals, educational
|
||||
@@ -0,0 +1,26 @@
|
||||
# vivid
|
||||
|
||||
Energetic, bold, attention-grabbing
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex |
|
||||
|------|-------|-----|
|
||||
| Primary 1 | Bright Red | #EF4444 |
|
||||
| Primary 2 | Neon Green | #22C55E |
|
||||
| Primary 3 | Electric Blue | #3B82F6 |
|
||||
| Background | Light Blue | #EFF6FF |
|
||||
| Background Alt | Soft Lavender | #F5F3FF |
|
||||
| Accent 1 | Bright Orange | #FB923C |
|
||||
| Accent 2 | Vivid Yellow | #FACC15 |
|
||||
|
||||
## Decorative Hints
|
||||
|
||||
- Dynamic diagonal lines and angles
|
||||
- Bold geometric shapes and color blocks
|
||||
- Dramatic lighting effects
|
||||
- High-energy visual compositions
|
||||
|
||||
## Best For
|
||||
|
||||
Product launch, gaming, promotion, event, marketing, announcements, brand showcases
|
||||
@@ -0,0 +1,26 @@
|
||||
# warm
|
||||
|
||||
Friendly, approachable, human-centered
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex |
|
||||
|------|-------|-----|
|
||||
| Primary 1 | Warm Orange | #ED8936 |
|
||||
| Primary 2 | Golden Yellow | #F6AD55 |
|
||||
| Primary 3 | Terracotta | #C05621 |
|
||||
| Background | Cream | #FFFAF0 |
|
||||
| Background Alt | Soft Peach | #FED7AA |
|
||||
| Accent 1 | Deep Brown | #744210 |
|
||||
| Accent 2 | Soft Red | #E53E3E |
|
||||
|
||||
## Decorative Hints
|
||||
|
||||
- Sun rays, warm lighting effects
|
||||
- Rounded shapes, organic curves
|
||||
- Hearts, smiling faces, friendly icons
|
||||
- Warm gradient overlays
|
||||
|
||||
## Best For
|
||||
|
||||
Personal growth, lifestyle, education, human stories, emotion, community
|
||||
@@ -0,0 +1,43 @@
|
||||
# chalk
|
||||
|
||||
Educational, authentic, classroom
|
||||
|
||||
## Core Characteristics
|
||||
|
||||
Chalk on blackboard aesthetic with imperfect strokes, dust effects, and authentic classroom feel. Nostalgic educational warmth.
|
||||
|
||||
## Lines
|
||||
|
||||
- Imperfect chalk strokes with variable pressure
|
||||
- Visible chalk texture and grain
|
||||
- Slightly wobbly, hand-drawn quality
|
||||
- Thick strokes for emphasis, thin for details
|
||||
|
||||
## Texture
|
||||
|
||||
- Chalk dust effects around text and elements
|
||||
- Board surface (dark, slightly worn)
|
||||
- Eraser smudges and residue
|
||||
- Grainy chalk quality on all elements
|
||||
|
||||
## Depth
|
||||
|
||||
- None: flat chalk drawings on board surface
|
||||
- Layering through erasure and redrawing
|
||||
- No shadows or perspective
|
||||
|
||||
## Element Vocabulary
|
||||
|
||||
- Chalk doodles: stars, arrows, underlines
|
||||
- Mathematical formulas and diagrams
|
||||
- Stick figures and simple icons
|
||||
- Connection lines with chalk feel
|
||||
- Checkmarks, circles, boxes for lists
|
||||
- Wooden frame border optional
|
||||
|
||||
## Typography Approach
|
||||
|
||||
- Hand-drawn chalk lettering
|
||||
- Imperfect baseline, authentic classroom feel
|
||||
- White or bright colored chalk for emphasis
|
||||
- Variable sizing for hierarchy
|
||||
@@ -0,0 +1,42 @@
|
||||
# digital
|
||||
|
||||
Polished, precise, modern
|
||||
|
||||
## Core Characteristics
|
||||
|
||||
Clean digital illustration with polished finish, precise edges, and subtle modern effects. Feels like a professional UI mockup or corporate illustration.
|
||||
|
||||
## Lines
|
||||
|
||||
- Clean, precise, computer-perfect edges
|
||||
- Consistent stroke weights
|
||||
- Sharp corners where appropriate
|
||||
- Anti-aliased smooth rendering
|
||||
|
||||
## Texture
|
||||
|
||||
- Smooth surfaces with no visible texture
|
||||
- Subtle gradients permitted (soft, controlled)
|
||||
- Frosted glass and blur effects
|
||||
- Clean shadows with consistent direction
|
||||
|
||||
## Depth
|
||||
|
||||
- Subtle gradients and soft drop shadows
|
||||
- Layered card-based layouts
|
||||
- Light 3D effects (subtle, not realistic)
|
||||
- Material Design-inspired elevation
|
||||
|
||||
## Element Vocabulary
|
||||
|
||||
- Polished icons and UI components
|
||||
- Data visualizations: charts, graphs, metrics
|
||||
- Card layouts and structured grids
|
||||
- Tag chips, progress bars, status indicators
|
||||
- Clean geometric shapes
|
||||
|
||||
## Typography Approach
|
||||
|
||||
- System UI or modern sans-serif (Inter, SF Pro style)
|
||||
- Clean, functional, high readability
|
||||
- Structured hierarchy with consistent spacing
|
||||
@@ -0,0 +1,42 @@
|
||||
# flat-vector
|
||||
|
||||
Clean, modern, geometric illustration
|
||||
|
||||
## Core Characteristics
|
||||
|
||||
Flat design with clean outlines, uniform fills, and no texture or depth. Think modern app icons, infographic illustrations, and vector-based editorial art.
|
||||
|
||||
## Lines
|
||||
|
||||
- Clean outlines with uniform stroke weight
|
||||
- Closed shapes (coloring-book style)
|
||||
- Rounded line endings, avoid sharp corners
|
||||
- Consistent stroke width throughout
|
||||
|
||||
## Texture
|
||||
|
||||
- None: smooth, flat color fills only
|
||||
- No gradients, shadows, or noise
|
||||
- Solid color blocks
|
||||
|
||||
## Depth
|
||||
|
||||
- Flat: no shadows, no perspective
|
||||
- 2D layering with overlap for depth illusion
|
||||
- Optional 2.5D isometric layering (front/back occlusion, no atmospheric perspective)
|
||||
- No 3D effects or bevels
|
||||
|
||||
## Element Vocabulary
|
||||
|
||||
- Geometric icons and simple shapes
|
||||
- Bold outlined objects with clean fills
|
||||
- Geometric simplification: complex objects → basic shapes (trees → lollipop/triangle, buildings → rectangles)
|
||||
- "Toy model" aesthetic: cute, rounded proportions
|
||||
- Decorative: dots, lines, sunbursts, pill-shaped clouds, small stars
|
||||
- Isolated elements on clean backgrounds
|
||||
|
||||
## Typography Approach
|
||||
|
||||
- Clean sans-serif or bold geometric lettering
|
||||
- Strong readability, consistent weight
|
||||
- Easily scalable at any size
|
||||
@@ -0,0 +1,40 @@
|
||||
# hand-drawn
|
||||
|
||||
Sketchy, organic, personal
|
||||
|
||||
## Core Characteristics
|
||||
|
||||
Hand-drawn illustration with visible imperfections, organic line quality, and personal touch. Feels like a skilled artist's sketchbook or whiteboard drawing.
|
||||
|
||||
## Lines
|
||||
|
||||
- Sketchy, organic, slightly imperfect strokes
|
||||
- Variable line weight (thicker at pressure points)
|
||||
- Wavy connectors and arrows
|
||||
- Natural hand tremor visible
|
||||
|
||||
## Texture
|
||||
|
||||
- Paper grain and subtle surface texture
|
||||
- Pencil/pen/marker texture on strokes
|
||||
- Casual fills with visible brush direction
|
||||
|
||||
## Depth
|
||||
|
||||
- Minimal: light hand-drawn shadows or hatching
|
||||
- No realistic depth or perspective
|
||||
- Simple layering with overlap
|
||||
|
||||
## Element Vocabulary
|
||||
|
||||
- Doodles, organic shapes, hand-lettered labels
|
||||
- Conceptual icons with sketchy quality
|
||||
- Connection lines with hand-drawn wavy feel
|
||||
- Stars, arrows, underlines, circles, checkmarks
|
||||
- Stick figures and simple characters
|
||||
|
||||
## Typography Approach
|
||||
|
||||
- Hand-lettered or marker-style text
|
||||
- Bouncy baselines, organic feel
|
||||
- Variable sizes for emphasis hierarchy
|
||||
@@ -0,0 +1,40 @@
|
||||
# painterly
|
||||
|
||||
Soft, artistic, expressive
|
||||
|
||||
## Core Characteristics
|
||||
|
||||
Watercolor or paint-style illustration with visible brush strokes, color bleeds, and artistic texture. Feels like a hand-painted art piece.
|
||||
|
||||
## Lines
|
||||
|
||||
- Soft brush strokes with variable opacity
|
||||
- No hard outlines; edges defined by color transitions
|
||||
- Organic flowing strokes with natural blending
|
||||
|
||||
## Texture
|
||||
|
||||
- Visible paint or watercolor wash textures
|
||||
- Color bleeds and wet-on-wet effects
|
||||
- Paper texture showing through transparent areas
|
||||
- Brush stroke patterns visible
|
||||
|
||||
## Depth
|
||||
|
||||
- Soft edges with natural color blending
|
||||
- Atmospheric depth through color fading
|
||||
- Layered washes creating depth illusion
|
||||
|
||||
## Element Vocabulary
|
||||
|
||||
- Watercolor washes as backgrounds
|
||||
- Natural elements: leaves, flowers, organic forms
|
||||
- Soft gradients and color transitions
|
||||
- Splatter and drip effects as accents
|
||||
- Botanical and environmental motifs
|
||||
|
||||
## Typography Approach
|
||||
|
||||
- Elegant brush script or handwritten style
|
||||
- Organic letterforms with brush texture
|
||||
- Integrated with paint environment
|
||||
@@ -0,0 +1,42 @@
|
||||
# pixel
|
||||
|
||||
Retro 8-bit, nostalgic, chunky
|
||||
|
||||
## Core Characteristics
|
||||
|
||||
Pixel art aesthetic with visible pixel grid, limited color palette, and nostalgic gaming feel. Emulates classic 8-bit and 16-bit era graphics.
|
||||
|
||||
## Lines
|
||||
|
||||
- Pixel grid alignment, no anti-aliasing
|
||||
- Staircase edges on diagonals
|
||||
- Single-pixel or double-pixel outlines
|
||||
- Blocky, angular forms
|
||||
|
||||
## Texture
|
||||
|
||||
- Dithering patterns for gradients
|
||||
- No smooth transitions
|
||||
- Cross-hatching with pixel precision
|
||||
- Limited 16-32 color palette per scene
|
||||
|
||||
## Depth
|
||||
|
||||
- None: flat pixel planes only
|
||||
- Parallax layering (foreground/background)
|
||||
- No perspective or 3D effects
|
||||
|
||||
## Element Vocabulary
|
||||
|
||||
- 8-bit sprites and chunky shapes
|
||||
- Simple iconography: stars, hearts, arrows
|
||||
- Text bubbles with pixel borders
|
||||
- Progress bars with chunky segments
|
||||
- Retro gaming UI elements
|
||||
|
||||
## Typography Approach
|
||||
|
||||
- Pixelated bitmap font style
|
||||
- Chunky blocky letterforms
|
||||
- Fixed-width or monospace feel
|
||||
- All-caps for headers
|
||||
@@ -0,0 +1,44 @@
|
||||
# screen-print
|
||||
|
||||
Bold, limited-color poster art with print texture
|
||||
|
||||
## Core Characteristics
|
||||
|
||||
Screen print / silkscreen aesthetic with flat color blocks, halftone textures, and deliberate print imperfections. Think Mondo limited-edition posters, vintage concert prints, and alternative movie poster art.
|
||||
|
||||
## Lines
|
||||
|
||||
- Clean, sharp edges between color blocks
|
||||
- No outlines — shapes defined by color boundaries
|
||||
- Stencil-cut quality, bold silhouettes
|
||||
- Geometric shapes with precise registration
|
||||
|
||||
## Texture
|
||||
|
||||
- Halftone dot patterns within color fills
|
||||
- Slight color layer misregistration (offset between print layers)
|
||||
- Paper grain texture beneath colors
|
||||
- Risograph / screen print imperfections (ink spread, dot gain)
|
||||
|
||||
## Depth
|
||||
|
||||
- Flat color planes layered front to back
|
||||
- Depth through silhouette overlap and color layering
|
||||
- No gradients — tonal variation via halftone density
|
||||
- Negative space as active compositional element
|
||||
|
||||
## Element Vocabulary
|
||||
|
||||
- Bold silhouettes and symbolic shapes
|
||||
- Geometric framing (circles, arches, triangles)
|
||||
- Figure-ground inversion (negative space forms secondary image)
|
||||
- Limited icon vocabulary: key props, symbolic objects
|
||||
- Typography integrated as design element, not overlay
|
||||
- Vintage poster border treatments
|
||||
|
||||
## Typography Approach
|
||||
|
||||
- Bold condensed sans-serif or hand-drawn lettering
|
||||
- Art Deco influences, vintage poster typography
|
||||
- Typography as integral part of composition (not separate layer)
|
||||
- Strong readability through high contrast with background
|
||||
@@ -0,0 +1,38 @@
|
||||
# Style Presets
|
||||
|
||||
`--style X` expands to a palette + rendering combination. Users can override either dimension.
|
||||
|
||||
| --style | Palette | Rendering |
|
||||
|---------|---------|-----------|
|
||||
| `elegant` | `elegant` | `hand-drawn` |
|
||||
| `blueprint` | `cool` | `digital` |
|
||||
| `chalkboard` | `dark` | `chalk` |
|
||||
| `dark-atmospheric` | `dark` | `digital` |
|
||||
| `editorial-infographic` | `cool` | `digital` |
|
||||
| `fantasy-animation` | `pastel` | `painterly` |
|
||||
| `flat-doodle` | `pastel` | `flat-vector` |
|
||||
| `intuition-machine` | `retro` | `digital` |
|
||||
| `minimal` | `mono` | `flat-vector` |
|
||||
| `nature` | `earth` | `hand-drawn` |
|
||||
| `notion` | `mono` | `digital` |
|
||||
| `pixel-art` | `vivid` | `pixel` |
|
||||
| `playful` | `pastel` | `hand-drawn` |
|
||||
| `retro` | `retro` | `digital` |
|
||||
| `sketch-notes` | `warm` | `hand-drawn` |
|
||||
| `vector-illustration` | `retro` | `flat-vector` |
|
||||
| `vintage` | `retro` | `hand-drawn` |
|
||||
| `warm` | `warm` | `hand-drawn` |
|
||||
| `warm-flat` | `warm` | `flat-vector` |
|
||||
| `watercolor` | `earth` | `painterly` |
|
||||
| `poster-art` | `retro` | `screen-print` |
|
||||
| `mondo` | `mono` | `screen-print` |
|
||||
| `art-deco` | `elegant` | `screen-print` |
|
||||
| `propaganda` | `vivid` | `screen-print` |
|
||||
| `cinematic` | `duotone` | `screen-print` |
|
||||
|
||||
## Override Examples
|
||||
|
||||
- `--style blueprint --rendering hand-drawn` = cool palette with hand-drawn rendering
|
||||
- `--style elegant --palette warm` = warm palette with hand-drawn rendering
|
||||
|
||||
Explicit `--palette`/`--rendering` flags always override preset values.
|
||||
@@ -1,25 +0,0 @@
|
||||
# blueprint
|
||||
|
||||
Precise technical blueprint style with engineering aesthetic
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Engineering Blue (#2563EB), Navy Blue (#1E3A5F)
|
||||
- Background: Blueprint Off-White (#FAF8F5), subtle grid overlay
|
||||
- Accents: Amber (#F59E0B), Light Blue (#BFDBFE)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Precise lines with consistent stroke weights
|
||||
- Technical schematics and clean vector graphics
|
||||
- Dimension lines and measurement indicators
|
||||
- Grid alignment for all elements
|
||||
- Geometric precision for all shapes
|
||||
|
||||
## Typography
|
||||
|
||||
- Clean sans-serif hand lettering, technical and authoritative
|
||||
|
||||
## Best For
|
||||
|
||||
Technical architecture, system design, data analysis, engineering documentation
|
||||
@@ -1,26 +0,0 @@
|
||||
# bold-editorial
|
||||
|
||||
High-impact magazine editorial with bold visual expression
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Pure White (#FFFFFF), Electric Blue (#3B82F6), Bright Orange (#FB923C)
|
||||
- Background: Deep Black (#0A0A0A), Deep Blue (#0F172A)
|
||||
- Accents: Magenta (#EC4899), Neon Green (#22C55E), Violet (#8B5CF6)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Strong typography as visual element itself
|
||||
- Geometric shapes and bold color blocks
|
||||
- Full-bleed solid color backgrounds
|
||||
- Dynamic diagonal lines and angles
|
||||
- Dramatic lighting effects on text
|
||||
- Minimal decoration, maximum impact
|
||||
|
||||
## Typography
|
||||
|
||||
- Bold condensed typeface, oversized headlines, all-caps, tight letter-spacing
|
||||
|
||||
## Best For
|
||||
|
||||
Product launches, marketing, keynote speeches, brand showcases, investor pitches
|
||||
@@ -1,61 +0,0 @@
|
||||
# chalkboard
|
||||
|
||||
Black chalkboard background with colorful chalk drawing style
|
||||
|
||||
## Design Aesthetic
|
||||
|
||||
Classic classroom chalkboard aesthetic with hand-drawn chalk illustrations. Nostalgic educational feel with imperfect, sketchy lines that capture the warmth of traditional teaching. Colorful chalk creates visual hierarchy while maintaining the authentic chalkboard experience.
|
||||
|
||||
## Background
|
||||
|
||||
- Color: Chalkboard Black (#1A1A1A) or Dark Green-Black (#1C2B1C)
|
||||
- Texture: Realistic chalkboard texture with subtle scratches, dust particles, and faint eraser marks
|
||||
|
||||
## Typography
|
||||
|
||||
Hand-drawn chalk lettering style with visible chalk texture. Imperfect baseline adds authenticity. White or bright colored chalk for emphasis.
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Hex | Usage |
|
||||
|------|-------|-----|-------|
|
||||
| Background | Chalkboard Black | #1A1A1A | Primary background |
|
||||
| Alt Background | Green-Black | #1C2B1C | Traditional green board |
|
||||
| Primary Text | Chalk White | #F5F5F5 | Main text, outlines |
|
||||
| Accent 1 | Chalk Yellow | #FFE566 | Highlights, emphasis |
|
||||
| Accent 2 | Chalk Pink | #FF9999 | Secondary highlights |
|
||||
| Accent 3 | Chalk Blue | #66B3FF | Diagrams, links |
|
||||
| Accent 4 | Chalk Green | #90EE90 | Success, nature |
|
||||
| Accent 5 | Chalk Orange | #FFB366 | Warnings, energy |
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Hand-drawn chalk illustrations with sketchy, imperfect lines
|
||||
- Chalk dust effects around text and key elements
|
||||
- Doodles: stars, arrows, underlines, circles, checkmarks
|
||||
- Mathematical formulas and simple diagrams
|
||||
- Eraser smudges and chalk residue textures
|
||||
- Wooden frame border optional
|
||||
- Stick figures and simple icons
|
||||
- Connection lines with hand-drawn feel
|
||||
|
||||
## Style Rules
|
||||
|
||||
### Do
|
||||
|
||||
- Maintain authentic chalk texture on all elements
|
||||
- Use imperfect, hand-drawn quality throughout
|
||||
- Add subtle chalk dust and smudge effects
|
||||
- Create visual hierarchy with color variety
|
||||
- Include playful doodles and annotations
|
||||
|
||||
### Don't
|
||||
|
||||
- Use perfect geometric shapes
|
||||
- Create clean digital-looking lines
|
||||
- Add photorealistic elements
|
||||
- Use gradients or glossy effects
|
||||
|
||||
## Best For
|
||||
|
||||
Educational content, tutorials, classroom themes, teaching materials, workshops, informal learning sessions, knowledge sharing
|
||||
@@ -1,25 +0,0 @@
|
||||
# dark-atmospheric
|
||||
|
||||
Dark moody aesthetic with glowing accent elements
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Electric Purple (#8B5CF6), Cyan Blue (#06B6D4), Magenta Pink (#EC4899)
|
||||
- Background: Deep Purple-Black (#0D0D1A), Rich Navy (#1A1A2E)
|
||||
- Accents: Amber (#F59E0B), Pure White (#FFFFFF)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Glowing accent elements and borders
|
||||
- Subtle gradient backgrounds
|
||||
- Atmospheric fog or particle effects
|
||||
- Neon-style highlights on key elements
|
||||
- Silhouettes with backlit edges
|
||||
|
||||
## Typography
|
||||
|
||||
- Elegant serif or refined sans-serif in light/white with subtle glow
|
||||
|
||||
## Best For
|
||||
|
||||
Entertainment, creative industries, premium brands, cinematic storytelling
|
||||
@@ -1,26 +0,0 @@
|
||||
# editorial-infographic
|
||||
|
||||
Modern magazine-style editorial with clear visual storytelling
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Near Black (#1A1A1A), Editorial Blue (#2563EB)
|
||||
- Background: Pure White (#FFFFFF), Light Gray (#F8F9FA)
|
||||
- Accents: Coral (#F97316), Emerald (#10B981), Amber (#F59E0B)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Clean flat illustrations (not photos)
|
||||
- Structured multi-section layouts
|
||||
- Callout boxes for key insights
|
||||
- Icon-based data visualization
|
||||
- Visual metaphors for abstract concepts
|
||||
- Pull quotes and highlight boxes
|
||||
|
||||
## Typography
|
||||
|
||||
- Bold display serif or modern sans-serif, editorial sophistication
|
||||
|
||||
## Best For
|
||||
|
||||
Technology explainers, science communication, research summaries, thought leadership
|
||||
@@ -1,23 +0,0 @@
|
||||
# elegant
|
||||
|
||||
Refined, sophisticated, understated
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Soft coral (#E8A598), muted teal (#5B8A8A), dusty rose (#D4A5A5)
|
||||
- Background: Warm cream (#F5F0E6), soft beige
|
||||
- Accents: Gold (#C9A962), copper
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Delicate lines, refined icons
|
||||
- Subtle gradients
|
||||
- Balanced composition
|
||||
|
||||
## Typography
|
||||
|
||||
- Elegant serif-style hand lettering
|
||||
|
||||
## Best For
|
||||
|
||||
Professional content, thought leadership, business topics
|
||||
@@ -1,25 +0,0 @@
|
||||
# fantasy-animation
|
||||
|
||||
Whimsical hand-drawn animation style inspired by Ghibli and Disney
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Golden Yellow (#F4D03F), Rose Pink (#E8A0BF), Deep Forest (#2D5A3D)
|
||||
- Background: Soft Sky Blue (#E8F4FC), Warm Cream (#FFF8E7)
|
||||
- Accents: Sage Green (#87A96B), Sky Blue (#7EC8E3), Coral (#F08080)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Central illustrated character (friendly, expressive)
|
||||
- Magical floating objects (books, orbs, sparkles)
|
||||
- Storybook-style environment backgrounds
|
||||
- Soft shadows and gentle highlights
|
||||
- Decorative elements: stars, sparkles, flowers, leaves
|
||||
|
||||
## Typography
|
||||
|
||||
- Whimsical serif or decorative hand-lettered style, organic feel
|
||||
|
||||
## Best For
|
||||
|
||||
Educational content, storytelling, creative workshops, fantasy/gaming content
|
||||
@@ -1,27 +0,0 @@
|
||||
# flat-doodle
|
||||
|
||||
Cute, simple doodle illustrations with bold outlines
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Bright pastel pink (#FFB6C1), mint (#98D8C8), lavender (#C8A2C8), butter yellow (#FFFACD)
|
||||
- Background: Clean white (#FFFFFF)
|
||||
- Accents: Bold black outlines, soft coral, sky blue
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Bold black outlines around all shapes
|
||||
- Simple flat shapes with no shading
|
||||
- Cute rounded proportions
|
||||
- Productivity-themed icons
|
||||
- Isolated elements on white background
|
||||
- Minimal decorative details
|
||||
|
||||
## Typography
|
||||
|
||||
- Simple, clean sans-serif or hand-lettering
|
||||
- Bold and easily readable
|
||||
|
||||
## Best For
|
||||
|
||||
Productivity content, SaaS articles, workflow tutorials, app features, beginner guides, casual business
|
||||
@@ -1,26 +0,0 @@
|
||||
# intuition-machine
|
||||
|
||||
Technical briefing style with aged paper and bilingual labels
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Dark Maroon (#5D3A3A), Teal (#2F7373)
|
||||
- Background: Aged Cream (#F5F0E6), subtle paper texture with light creases
|
||||
- Accents: Warm Brown (#8B7355), Maroon (#722F37), Deep Charcoal (#2D2D2D)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Isometric 3D technical illustrations or flat 2D diagrams
|
||||
- Bilingual callout labels (English + Chinese)
|
||||
- Faded thematic background patterns
|
||||
- Clean black outlines on all elements
|
||||
- Split or triptych layouts
|
||||
- Key quote box at bottom
|
||||
|
||||
## Typography
|
||||
|
||||
- Bold display font in dark maroon, ALL CAPS for titles, bilingual labels
|
||||
|
||||
## Best For
|
||||
|
||||
Technical explanations, academic presentations, bilingual audiences, knowledge docs
|
||||
@@ -1,23 +0,0 @@
|
||||
# minimal
|
||||
|
||||
Ultra-clean, zen-like, focused
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Pure black (#000000), white (#FFFFFF)
|
||||
- Background: White or off-white (#FAFAFA)
|
||||
- Accents: Single color (user's choice or content-derived)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Single focal point
|
||||
- Maximum negative space
|
||||
- Thin lines
|
||||
|
||||
## Typography
|
||||
|
||||
- Clean, simple hand lettering, lots of breathing room
|
||||
|
||||
## Best For
|
||||
|
||||
Philosophy, minimalism, focused concepts
|
||||
@@ -1,22 +0,0 @@
|
||||
# nature
|
||||
|
||||
Organic, calm, earthy
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Forest green (#276749), sage (#9AE6B4), earth brown (#744210)
|
||||
- Background: Sand beige (#F5E6D3), sky blue (#E0F2FE)
|
||||
- Accents: Sunset orange, water blue
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Leaves, trees, mountains
|
||||
- Sun, clouds, organic flowing lines
|
||||
|
||||
## Typography
|
||||
|
||||
- Organic, flowing hand lettering with natural textures
|
||||
|
||||
## Best For
|
||||
|
||||
Sustainability, wellness, outdoor topics, slow living
|
||||
@@ -1,25 +0,0 @@
|
||||
# notion
|
||||
|
||||
Clean SaaS dashboard aesthetic with productivity tool styling
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Near Black (#1F1F1F), Notion Blue (#2383E2)
|
||||
- Background: Light Gray (#F7F7F5), Pure White (#FFFFFF)
|
||||
- Accents: Success Green (#0F7B6C), Alert Red (#E03E3E), Warning Yellow (#DFAB01)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Card-based layouts with subtle borders
|
||||
- Clean data visualizations
|
||||
- Tag and label chips
|
||||
- Icon-based navigation hints
|
||||
- Progress bars and metric displays
|
||||
|
||||
## Typography
|
||||
|
||||
- System UI or Inter style, clean and functional
|
||||
|
||||
## Best For
|
||||
|
||||
Product demos, SaaS presentations, productivity content, B2B topics
|
||||
@@ -1,26 +0,0 @@
|
||||
# pixel-art
|
||||
|
||||
Retro 8-bit pixel art aesthetic with nostalgic gaming style
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Dark Navy (#1A1A2E), Pixel Green (#00FF00)
|
||||
- Background: Light Blue (#87CEEB), Soft Lavender (#E6E6FA)
|
||||
- Accents: Pixel Red (#FF0000), Pixel Yellow (#FFFF00), Pixel Cyan (#00FFFF)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- All elements with visible pixel structure
|
||||
- Simple iconography: stars, hearts, arrows
|
||||
- Text bubbles with pixel borders
|
||||
- Progress bars with chunky segments
|
||||
- Dithering patterns for gradients
|
||||
- Limited 16-32 color palette
|
||||
|
||||
## Typography
|
||||
|
||||
- Pixelated bitmap font style, chunky blocky letterforms
|
||||
|
||||
## Best For
|
||||
|
||||
Gaming content, tech tutorials, developer talks, retro-themed topics
|
||||
@@ -1,23 +0,0 @@
|
||||
# playful
|
||||
|
||||
Fun, creative, whimsical
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Pastel pink (#FED7E2), mint (#C6F6D5), lavender (#E9D8FD), sky blue (#BEE3F8)
|
||||
- Background: Light cream (#FFFBEB), soft white
|
||||
- Accents: Bright pops - yellow, coral, turquoise
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Doodles, stars, swirls
|
||||
- Cute characters, emoji-style icons
|
||||
- Playful compositions
|
||||
|
||||
## Typography
|
||||
|
||||
- Bouncy, irregular hand lettering, playful angles
|
||||
|
||||
## Best For
|
||||
|
||||
Casual content, tutorials, beginner guides, fun topics
|
||||
@@ -1,22 +0,0 @@
|
||||
# retro
|
||||
|
||||
Vintage, nostalgic, classic
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Muted orange (#ED8936 at 70%), dusty pink (#FED7E2 at 80%), faded teal
|
||||
- Background: Aged paper (#F5E6D3), sepia tones
|
||||
- Accents: Faded red, vintage gold
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Halftone dots, vintage badges
|
||||
- Classic icons, aged textures
|
||||
|
||||
## Typography
|
||||
|
||||
- Vintage-style hand lettering, classic serif influence
|
||||
|
||||
## Best For
|
||||
|
||||
History, retrospectives, classic topics, throwback content
|
||||
@@ -1,25 +0,0 @@
|
||||
# sketch-notes
|
||||
|
||||
Soft hand-drawn illustration style with fresh minimalist aesthetic
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Deep Charcoal (#2C3E50), Soft Orange (#F4A261)
|
||||
- Background: Warm Off-White (#FAF8F0), subtle paper grain
|
||||
- Accents: Mustard Yellow (#E9C46A), Sage Green (#87A96B), Light Blue (#7EC8E3)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Connection lines with hand-drawn wavy feel
|
||||
- Conceptual abstract icons illustrating ideas
|
||||
- Color fills with hand-painted casual feel
|
||||
- Doodle-style decorative elements
|
||||
- Arrows and pointers with sketchy style
|
||||
|
||||
## Typography
|
||||
|
||||
- Bold hand-written marker font for headlines, casual handwriting for body
|
||||
|
||||
## Best For
|
||||
|
||||
Educational content, knowledge sharing, technical explanations, tutorials
|
||||
@@ -1,25 +0,0 @@
|
||||
# vector-illustration
|
||||
|
||||
Flat vector illustration with black outlines and retro soft colors
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Coral Red (#E07A5F), Mint Green (#81B29A), Mustard Yellow (#F2CC8F)
|
||||
- Background: Cream Off-White (#F5F0E6), subtle paper texture
|
||||
- Accents: Burnt Orange (#D4764A), Rock Blue (#577590), Deep Charcoal (#2D2D2D) outlines
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- All objects have closed black outlines (coloring book style)
|
||||
- Rounded line endings, avoid sharp corners
|
||||
- Simplified geometric shapes
|
||||
- 2.5D perspective with layering
|
||||
- Decorative elements: sunbursts, pill clouds, dots, stars
|
||||
|
||||
## Typography
|
||||
|
||||
- Large bold retro serif for titles, clean geometric sans-serif for body
|
||||
|
||||
## Best For
|
||||
|
||||
Educational content, creative proposals, brand showcases, explainer content
|
||||
@@ -1,26 +0,0 @@
|
||||
# vintage
|
||||
|
||||
Vintage aged-paper aesthetic with historical document styling
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Dark Brown (#3D2914), Forest Green (#2D5A3D), Navy Blue (#1E3A5F)
|
||||
- Background: Aged Parchment (#F5E6D3), Sepia Cream (#FFF8DC)
|
||||
- Accents: Burgundy (#722F37), Gold (#C9A227), Sepia Black (#3D3D3D)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Antique maps with route lines and landmarks
|
||||
- Compass roses and nautical elements
|
||||
- Specimen drawings (flora, fauna)
|
||||
- Handwritten-style annotations
|
||||
- Rope, leather, brass decorative motifs
|
||||
- Aged texture with subtle creases
|
||||
|
||||
## Typography
|
||||
|
||||
- Classic serif with historical character, elegant flourishes
|
||||
|
||||
## Best For
|
||||
|
||||
Historical content, travel, heritage storytelling, biography, scientific discovery
|
||||
@@ -1,22 +0,0 @@
|
||||
# warm
|
||||
|
||||
Friendly, approachable, human-centered
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Warm orange (#ED8936), golden yellow (#F6AD55), terracotta (#C05621)
|
||||
- Background: Cream (#FFFAF0), soft peach (#FED7AA)
|
||||
- Accents: Deep brown (#744210), soft red
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Rounded shapes, smiling faces
|
||||
- Sun rays, hearts, warm lighting
|
||||
|
||||
## Typography
|
||||
|
||||
- Friendly rounded hand lettering
|
||||
|
||||
## Best For
|
||||
|
||||
Personal growth, lifestyle, education, human stories
|
||||
@@ -1,25 +0,0 @@
|
||||
# watercolor
|
||||
|
||||
Soft watercolor illustration style with hand-painted textures
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Primary: Soft Coral (#F4A261), Dusty Rose (#E8A0A0)
|
||||
- Background: Warm Off-White (#FAF8F0), Soft Cream (#FFF9E6)
|
||||
- Accents: Sage Green (#87A96B), Sky Blue (#7EC8E3), Soft Lavender (#C5B4E3)
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Watercolor washes as backgrounds
|
||||
- Visible brush strokes and textures
|
||||
- Natural elements: leaves, flowers, organic shapes
|
||||
- Color bleeds and soft edges
|
||||
- Hand-drawn arrows and connections
|
||||
|
||||
## Typography
|
||||
|
||||
- Elegant handwritten or brush script, organic letterforms
|
||||
|
||||
## Best For
|
||||
|
||||
Lifestyle content, wellness, travel guides, personal stories, creative topics
|
||||
@@ -0,0 +1,23 @@
|
||||
# Type Composition Guidelines
|
||||
|
||||
## Type Gallery
|
||||
|
||||
| Type | Description | Best For |
|
||||
|------|-------------|----------|
|
||||
| `hero` | Large visual impact, title overlay | Product launch, brand promotion, major announcements |
|
||||
| `conceptual` | Concept visualization, abstract core ideas | Technical articles, methodology, architecture design |
|
||||
| `typography` | Text-focused layout, prominent title | Opinion pieces, quotes, insights |
|
||||
| `metaphor` | Visual metaphor, concrete expressing abstract | Philosophy, growth, personal development |
|
||||
| `scene` | Atmospheric scene, narrative feel | Stories, travel, lifestyle |
|
||||
| `minimal` | Minimalist composition, generous whitespace | Zen, focus, core concepts |
|
||||
|
||||
## Type-Specific Composition
|
||||
|
||||
| Type | Composition Guidelines |
|
||||
|------|------------------------|
|
||||
| `hero` | Large focal visual (60-70% area), title overlay on visual, dramatic composition |
|
||||
| `conceptual` | Abstract shapes representing core concepts, information hierarchy, clean zones |
|
||||
| `typography` | Title as primary element (40%+ area), minimal supporting visuals, strong hierarchy |
|
||||
| `metaphor` | Concrete object/scene representing abstract idea, symbolic elements, emotional resonance |
|
||||
| `scene` | Atmospheric environment, narrative elements, mood-setting lighting and colors |
|
||||
| `minimal` | Single focal element, generous whitespace (60%+), essential shapes only |
|
||||
@@ -0,0 +1,101 @@
|
||||
# Visual Elements Library
|
||||
|
||||
Icon and symbol vocabulary organized by topic. Use these as building blocks for cover compositions.
|
||||
|
||||
## Tech & Development
|
||||
|
||||
| Element | Use For |
|
||||
|---------|---------|
|
||||
| Code window / Terminal | Programming, development |
|
||||
| Gear / Cog | Engineering, settings, process |
|
||||
| Circuit board / Chip | Hardware, AI, computing |
|
||||
| Binary / Data stream | Data, algorithms |
|
||||
| API brackets `</>` | Web development, APIs |
|
||||
| Cloud | Cloud computing, SaaS |
|
||||
| Lock / Shield | Security, privacy |
|
||||
| Network nodes | Distributed systems, connections |
|
||||
|
||||
## Ideas & Innovation
|
||||
|
||||
| Element | Use For |
|
||||
|---------|---------|
|
||||
| Lightbulb | Ideas, insights, innovation |
|
||||
| Rocket | Launch, growth, startups |
|
||||
| Target / Bullseye | Goals, precision, focus |
|
||||
| Puzzle piece | Problem solving, integration |
|
||||
| Key | Solutions, access, unlocking |
|
||||
| Magnifying glass | Analysis, search, discovery |
|
||||
| Chart / Graph | Data, trends, growth |
|
||||
| Arrow / Path | Direction, journey, progress |
|
||||
|
||||
## Communication & Collaboration
|
||||
|
||||
| Element | Use For |
|
||||
|---------|---------|
|
||||
| Speech bubble | Communication, dialogue |
|
||||
| Chat dots `...` | Conversation, messaging |
|
||||
| Handshake | Partnership, agreement |
|
||||
| Team / Figures | Collaboration, community |
|
||||
| Mail / Envelope | Notifications, outreach |
|
||||
| Megaphone | Announcements, marketing |
|
||||
| Network / Web | Social, connections |
|
||||
|
||||
## Nature & Growth
|
||||
|
||||
| Element | Use For |
|
||||
|---------|---------|
|
||||
| Plant / Sprout | Growth, organic, sustainability |
|
||||
| Tree | Established, structure, branching |
|
||||
| Leaf | Eco, natural, fresh |
|
||||
| Sun / Rays | Energy, positivity, new beginnings |
|
||||
| Mountain | Challenge, achievement, scale |
|
||||
| Wave | Flow, change, rhythm |
|
||||
| Seed → Plant | Transformation, potential |
|
||||
|
||||
## Tools & Actions
|
||||
|
||||
| Element | Use For |
|
||||
|---------|---------|
|
||||
| Wrench / Hammer | Building, fixing, tools |
|
||||
| Pencil / Pen | Writing, creation, editing |
|
||||
| Brush | Design, creativity, art |
|
||||
| Scissors | Cutting, editing, trimming |
|
||||
| Clock / Timer | Time, scheduling, deadlines |
|
||||
| Calendar | Planning, events, milestones |
|
||||
| Checklist / Checkbox | Tasks, completion, validation |
|
||||
|
||||
## Abstract Concepts
|
||||
|
||||
| Element | Use For |
|
||||
|---------|---------|
|
||||
| Infinity ∞ | Continuous, endless, loops |
|
||||
| Yin-yang | Balance, duality, harmony |
|
||||
| Spiral | Evolution, recursion, cycles |
|
||||
| Stack / Layers | Depth, hierarchy, structure |
|
||||
| Bridge | Connection, transition, spanning |
|
||||
| Door / Portal | Opportunity, entry, access |
|
||||
| Mirror / Reflection | Self-improvement, analysis |
|
||||
|
||||
## Combination Patterns
|
||||
|
||||
Create visual metaphors by combining elements:
|
||||
|
||||
| Combination | Represents |
|
||||
|-------------|------------|
|
||||
| Lightbulb + Gear | Innovative engineering |
|
||||
| Plant + Code | Organic tech growth |
|
||||
| Rocket + Target | Precise acceleration |
|
||||
| Key + Lock | Security solutions |
|
||||
| Bridge + People | Team connections |
|
||||
| Magnifier + Data | Analytics, insights |
|
||||
|
||||
## Rendering-Specific Treatment
|
||||
|
||||
| Rendering | Element Style |
|
||||
|-----------|---------------|
|
||||
| `flat-vector` | Geometric, simple shapes, uniform fills |
|
||||
| `hand-drawn` | Sketchy, organic, doodle-like |
|
||||
| `painterly` | Soft edges, brush strokes |
|
||||
| `digital` | Precise, gradient hints, polished |
|
||||
| `pixel` | 8-bit chunky, grid-aligned |
|
||||
| `chalk` | Dusty, textured, board style |
|
||||
@@ -0,0 +1,152 @@
|
||||
# Step 2: Confirm Options
|
||||
|
||||
## Purpose
|
||||
|
||||
Validate all 6 dimensions + aspect ratio.
|
||||
|
||||
## Skip Conditions
|
||||
|
||||
| Condition | Skipped Questions | Still Asked |
|
||||
|-----------|-------------------|-------------|
|
||||
| `--quick` flag | Type, Palette, Rendering, Text, Mood, Font | **Aspect Ratio** (unless `--aspect` specified) |
|
||||
| All 6 dimensions + `--aspect` specified | All | None |
|
||||
| `quick_mode: true` in EXTEND.md | Type, Palette, Rendering, Text, Mood, Font | **Aspect Ratio** (unless `--aspect` specified) |
|
||||
| Otherwise | None | All 7 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 6 dimensions:
|
||||
|
||||
```
|
||||
Quick Mode: Auto-selected dimensions
|
||||
• Type: [type] ([reason])
|
||||
• Palette: [palette] ([reason])
|
||||
• Rendering: [rendering] ([reason])
|
||||
• Text: [text] ([reason])
|
||||
• Mood: [mood] ([reason])
|
||||
• Font: [font] ([reason])
|
||||
|
||||
[Then ask Question 7: Aspect Ratio]
|
||||
```
|
||||
|
||||
## Confirmation Flow
|
||||
|
||||
**Language**: Auto-determined (user's input language > saved preference > source language). No need to ask.
|
||||
|
||||
Present ALL options in a **single AskUserQuestion call** (4 questions max).
|
||||
|
||||
Skip any question where the dimension is already specified via CLI flag or `--style` preset.
|
||||
|
||||
### Q1: Type (skip if `--type`)
|
||||
|
||||
```yaml
|
||||
header: "Type"
|
||||
question: "Which cover type?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "[auto-recommended type] (Recommended)"
|
||||
description: "[reason based on content signals]"
|
||||
- label: "hero"
|
||||
description: "Large visual impact, title overlay - product launch, announcements"
|
||||
- label: "conceptual"
|
||||
description: "Concept visualization - technical, architecture"
|
||||
- label: "typography"
|
||||
description: "Text-focused layout - opinions, quotes"
|
||||
```
|
||||
|
||||
### Q2: Palette (skip if `--palette` or `--style`)
|
||||
|
||||
```yaml
|
||||
header: "Palette"
|
||||
question: "Which color palette?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "[auto-recommended palette] (Recommended)"
|
||||
description: "[reason based on content signals]"
|
||||
- label: "warm"
|
||||
description: "Friendly - orange, golden yellow, terracotta"
|
||||
- label: "elegant"
|
||||
description: "Sophisticated - soft coral, muted teal, dusty rose"
|
||||
- label: "cool"
|
||||
description: "Technical - engineering blue, navy, cyan"
|
||||
```
|
||||
|
||||
### Q3: Rendering (skip if `--rendering` or `--style`)
|
||||
|
||||
Show compatible renderings (✓✓ first from compatibility matrix):
|
||||
|
||||
```yaml
|
||||
header: "Rendering"
|
||||
question: "Which rendering style?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "[best compatible rendering] (Recommended)"
|
||||
description: "[reason based on palette + type + content]"
|
||||
- label: "flat-vector"
|
||||
description: "Clean outlines, flat fills, geometric icons"
|
||||
- label: "hand-drawn"
|
||||
description: "Sketchy, organic, imperfect strokes"
|
||||
- label: "digital"
|
||||
description: "Polished, precise, subtle gradients"
|
||||
```
|
||||
|
||||
### Q4: Font (skip if `--font`)
|
||||
|
||||
```yaml
|
||||
header: "Font"
|
||||
question: "Which font style?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "[auto-recommended font] (Recommended)"
|
||||
description: "[reason based on content signals]"
|
||||
- label: "clean"
|
||||
description: "Modern geometric sans-serif - tech, professional"
|
||||
- label: "handwritten"
|
||||
description: "Warm hand-lettered - personal, friendly"
|
||||
- label: "serif"
|
||||
description: "Classic elegant - editorial, luxury"
|
||||
- label: "display"
|
||||
description: "Bold decorative - announcements, entertainment"
|
||||
```
|
||||
|
||||
### Q5: Other Settings (skip if all remaining dimensions already specified)
|
||||
|
||||
Combine remaining settings into one question. Include: Output Dir (if no preference + file path input), Text, Mood, Aspect. Show auto-selected values as recommended option. User can accept all or type adjustments via "Other".
|
||||
|
||||
**When output dir needs asking** (no `default_output_dir` preference + file path input):
|
||||
|
||||
```yaml
|
||||
header: "Settings"
|
||||
question: "Output / Text / Mood / Aspect?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "imgs/ / [auto-text] / [auto-mood] / [preset-aspect] (Recommended)"
|
||||
description: "{article-dir}/imgs/, [text reason], [mood reason], [aspect source]"
|
||||
- label: "same-dir / [auto-text] / [auto-mood] / [preset-aspect]"
|
||||
description: "{article-dir}/, same directory as article"
|
||||
- label: "independent / [auto-text] / [auto-mood] / [preset-aspect]"
|
||||
description: "cover-image/{topic-slug}/, separate from article"
|
||||
```
|
||||
|
||||
**When output dir already set** (preference exists or pasted content):
|
||||
|
||||
```yaml
|
||||
header: "Settings"
|
||||
question: "Text / Mood / Aspect?"
|
||||
multiSelect: false
|
||||
options:
|
||||
- label: "[auto-text] / [auto-mood] / [preset-aspect] (Recommended)"
|
||||
description: "Auto-selected: [text reason], [mood reason], [aspect source]"
|
||||
- label: "[auto-text] / bold / [preset-aspect]"
|
||||
description: "High contrast, vivid — matches [content signal]"
|
||||
- label: "[auto-text] / subtle / [preset-aspect]"
|
||||
description: "Low contrast, muted — calm, professional"
|
||||
```
|
||||
|
||||
*Note*: "Other" (auto-added) allows typing custom combo. Parse `/`-separated values matching the question format.
|
||||
|
||||
## After Response
|
||||
|
||||
Proceed to Step 3 with confirmed dimensions.
|
||||
@@ -0,0 +1,247 @@
|
||||
# Step 3: Prompt Template
|
||||
|
||||
Save to `prompts/cover.md`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: cover
|
||||
palette: [confirmed palette]
|
||||
rendering: [confirmed rendering]
|
||||
references:
|
||||
- ref_id: 01
|
||||
filename: refs/ref-01-{slug}.{ext}
|
||||
usage: direct | style | palette
|
||||
- ref_id: 02
|
||||
filename: refs/ref-02-{slug}.{ext}
|
||||
usage: direct | style | palette
|
||||
---
|
||||
|
||||
# Content Context
|
||||
Article title: [full original title from source]
|
||||
Content summary: [2-3 sentence summary of key points and themes]
|
||||
Keywords: [5-8 key terms extracted from content]
|
||||
|
||||
# Visual Design
|
||||
Cover theme: [2-3 words visual interpretation]
|
||||
Type: [confirmed type]
|
||||
Palette: [confirmed palette]
|
||||
Rendering: [confirmed rendering]
|
||||
Font: [confirmed font]
|
||||
Text level: [confirmed text level]
|
||||
Mood: [confirmed mood]
|
||||
Aspect ratio: [confirmed ratio]
|
||||
Language: [confirmed language]
|
||||
|
||||
# Text Elements
|
||||
[Based on text level:]
|
||||
- none: "No text elements"
|
||||
- title-only: "Title: [exact title from source or user]"
|
||||
- title-subtitle: "Title: [title] / Subtitle: [context]"
|
||||
- text-rich: "Title: [title] / 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"
|
||||
|
||||
# Font Application
|
||||
[Based on font style:]
|
||||
- clean: "Use clean geometric sans-serif typography. Modern, minimal letterforms."
|
||||
- handwritten: "Use warm hand-lettered typography with organic brush strokes. Friendly, personal feel."
|
||||
- serif: "Use elegant serif typography with refined letterforms. Classic, editorial character."
|
||||
- display: "Use bold decorative display typography. Heavy, expressive headlines."
|
||||
|
||||
# Composition
|
||||
Type composition:
|
||||
- [Type-specific layout and structure]
|
||||
|
||||
Visual composition:
|
||||
- Main visual: [metaphor derived from content meaning]
|
||||
- Layout: [positioning based on type and aspect ratio]
|
||||
- Decorative: [palette-specific elements that reinforce content theme]
|
||||
|
||||
Color scheme: [primary, background, accent from palette definition, adjusted by mood]
|
||||
Rendering notes: [key characteristics from rendering definition — lines, texture, depth, element style]
|
||||
Type notes: [key characteristics from type definition]
|
||||
Palette notes: [key characteristics from palette definition]
|
||||
|
||||
[Watermark section if enabled]
|
||||
|
||||
[Reference images section if provided — REQUIRED, see below]
|
||||
```
|
||||
|
||||
## Reference-Driven Design ⚠️ HIGH PRIORITY
|
||||
|
||||
When reference images are provided, they are the **primary visual input** and MUST strongly influence the output. The cover should look like it belongs to the same visual family as the references.
|
||||
|
||||
**Passing `--ref` alone is NOT enough.** Image generation models often ignore reference images unless the prompt text explicitly describes what to reproduce. Always combine `--ref` with detailed textual instructions.
|
||||
|
||||
## 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
|
||||
|
||||
## Visual Element Selection
|
||||
|
||||
Match content themes to icon vocabulary:
|
||||
|
||||
| Content Theme | Suggested Elements |
|
||||
|---------------|-------------------|
|
||||
| Programming/Dev | Code window, terminal, API brackets, gear |
|
||||
| AI/ML | Brain, neural network, robot, circuit |
|
||||
| Growth/Business | Chart, rocket, plant, mountain, arrow |
|
||||
| Security | Lock, shield, key, fingerprint |
|
||||
| Communication | Speech bubble, megaphone, mail, handshake |
|
||||
| Tools/Methods | Wrench, checklist, pencil, puzzle |
|
||||
|
||||
Full library: [../visual-elements.md](../visual-elements.md)
|
||||
|
||||
## Type-Specific Composition
|
||||
|
||||
| Type | Composition Guidelines |
|
||||
|------|------------------------|
|
||||
| `hero` | Large focal visual (60-70% area), title overlay on visual, dramatic composition |
|
||||
| `conceptual` | Abstract shapes representing core concepts, information hierarchy, clean zones |
|
||||
| `typography` | Title as primary element (40%+ area), minimal supporting visuals, strong hierarchy |
|
||||
| `metaphor` | Concrete object/scene representing abstract idea, symbolic elements, emotional resonance |
|
||||
| `scene` | Atmospheric environment, narrative elements, mood-setting lighting and colors |
|
||||
| `minimal` | Single focal element, generous whitespace (60%+), essential shapes only |
|
||||
|
||||
## Title Guidelines
|
||||
|
||||
When text level includes title:
|
||||
- **Source**: Use the exact title provided by user, or extract from source content
|
||||
- **Do NOT invent titles**: Stay faithful to the original
|
||||
- Match confirmed language
|
||||
|
||||
## Watermark Application
|
||||
|
||||
If enabled in preferences, add to prompt:
|
||||
|
||||
```
|
||||
Include a subtle watermark "[content]" positioned at [position].
|
||||
The watermark should be legible but not distracting from the main content.
|
||||
```
|
||||
|
||||
Reference: `config/watermark-guide.md`
|
||||
|
||||
## Reference Image Handling
|
||||
|
||||
When user provides reference images (`--ref` or pasted images):
|
||||
|
||||
### ⚠️ CRITICAL - Frontmatter References
|
||||
|
||||
**MUST add `references` field in YAML frontmatter** when reference files are saved to `refs/`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
type: cover
|
||||
palette: warm
|
||||
rendering: flat-vector
|
||||
references:
|
||||
- ref_id: 01
|
||||
filename: refs/ref-01-podcast-thumbnail.jpg
|
||||
usage: style
|
||||
---
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `ref_id` | Sequential number (01, 02, ...) |
|
||||
| `filename` | Relative path from prompt file's parent directory |
|
||||
| `usage` | `direct` / `style` / `palette` |
|
||||
|
||||
**Omit `references` field entirely** if no reference files saved (style extracted verbally only).
|
||||
|
||||
### When to Include References in Frontmatter
|
||||
|
||||
| Situation | Frontmatter Action | Generation Action |
|
||||
|-----------|-------------------|-------------------|
|
||||
| Reference file saved to `refs/` | Add to `references` list ✓ | Pass via `--ref` parameter |
|
||||
| Style extracted verbally (no file) | Omit `references` field | Describe in prompt body only |
|
||||
| File path in frontmatter but doesn't exist | ERROR - fix or remove | Generation will fail |
|
||||
|
||||
**Before writing prompt with references, verify**: `test -f refs/ref-NN-{slug}.{ext}`
|
||||
|
||||
### Reference Usage Types
|
||||
|
||||
| Usage | When to Use | Generation Action |
|
||||
|-------|-------------|-------------------|
|
||||
| `direct` | Reference matches desired output closely | Pass to `--ref` parameter |
|
||||
| `style` | Extract visual style characteristics only | Describe style in prompt text |
|
||||
| `palette` | Extract color palette only | Include colors in prompt |
|
||||
|
||||
### Step 1: Analyze References
|
||||
|
||||
For each reference image, extract:
|
||||
- **Style**: Rendering technique, line quality, texture
|
||||
- **Composition**: Layout, visual hierarchy, focal points
|
||||
- **Color mood**: Palette characteristics (without specific colors)
|
||||
- **Elements**: Key visual elements and symbols used
|
||||
|
||||
### Step 2: Embed in Prompt ⚠️ CRITICAL
|
||||
|
||||
**Passing `--ref` alone is NOT enough.** Image generation models frequently ignore reference images unless the prompt text explicitly and forcefully describes what to reproduce. You MUST always write detailed textual instructions regardless of whether `--ref` is used.
|
||||
|
||||
**If file saved (with or without `--ref` support)**:
|
||||
- Pass ref images via `--ref` parameter if skill supports it
|
||||
- **ALWAYS** add a detailed mandatory section in the prompt body:
|
||||
|
||||
```
|
||||
# Reference Style — MUST INCORPORATE
|
||||
|
||||
CRITICAL: The generated cover MUST visually reference the provided images. The cover must feel like it belongs to the same visual family.
|
||||
|
||||
## From Ref 1 ([filename]) — REQUIRED elements:
|
||||
- [Brand element]: [Specific description of logo/wordmark treatment, e.g., "The logo uses vertical parallel lines (|||) for the letter 'm'. Reproduce this exact treatment."]
|
||||
- [Signature pattern]: [Specific description, e.g., "Woven intersecting curves forming a diamond/lozenge grid pattern. This MUST appear prominently as a banner, border, or background section."]
|
||||
- [Colors]: [Exact hex values, e.g., "Dark teal #2D4A3E background, cream #F5F0E0 text"]
|
||||
- [Typography]: [Specific treatment, e.g., "Uppercase text with wide letter-spacing"]
|
||||
- [Layout element]: [Specific spatial element, e.g., "Bottom banner strip in dark color"]
|
||||
|
||||
## From Ref 2 ([filename]) — REQUIRED elements:
|
||||
[Same detailed breakdown]
|
||||
|
||||
## Integration approach:
|
||||
[Specific layout instruction describing how reference elements combine with the cover content, e.g., "Use a SPLIT LAYOUT: main illustration area (warm cream background) occupies ~65% of the image, while a dark teal BANNER STRIP (with the woven line pattern from Ref 2) runs along the bottom ~35%, containing branding elements from Ref 1."]
|
||||
```
|
||||
|
||||
**Key rules**:
|
||||
- Each visual element gets its own bullet with "MUST" or "REQUIRED"
|
||||
- Descriptions must be **specific enough to reproduce** — not vague ("clean style")
|
||||
- The integration approach must describe **exact spatial arrangement**
|
||||
- After generation, verify reference elements are visible; if not, strengthen and regenerate
|
||||
|
||||
**If style/palette extracted verbally (NO file saved)**:
|
||||
- DO NOT add references metadata to prompt
|
||||
- Append extracted info directly to prompt body using the same MUST INCORPORATE format above:
|
||||
|
||||
```
|
||||
# Reference Style — MUST INCORPORATE (extracted from visual analysis)
|
||||
|
||||
CRITICAL: Apply these specific visual elements extracted from the reference images.
|
||||
|
||||
## REQUIRED elements:
|
||||
- [Same detailed bullet format as above]
|
||||
|
||||
## Integration approach:
|
||||
[Same spatial layout instruction]
|
||||
```
|
||||
|
||||
### Reference Analysis Template
|
||||
|
||||
Use this format when analyzing reference images. Extract **specific, concrete, reproducible** details — not vague summaries.
|
||||
|
||||
| Aspect | Analysis Points | Good Example | Bad Example |
|
||||
|--------|-----------------|--------------|-------------|
|
||||
| **Brand elements** | Logos, wordmarks, distinctive typography | "Logo 'm' formed by 3 vertical lines" | "Has a logo" |
|
||||
| **Signature patterns** | Unique motifs, textures, geometric patterns | "Woven curves forming diamond grid" | "Has patterns" |
|
||||
| **Colors** | Exact hex values or close approximations | "#2D4A3E dark teal, #F5F0E0 cream" | "Dark and light" |
|
||||
| **Layout** | Spatial zones, banner placement, proportions | "Bottom 30% is dark banner with branding" | "Has a banner" |
|
||||
| **Typography** | Font style, weight, case, spacing, position | "Uppercase, wide letter-spacing, right-aligned" | "Has text" |
|
||||
| **Rendering** | Line quality, texture, depth treatment | "Topographic contour lines as background texture" | "Clean style" |
|
||||
| **Elements** | Icon vocabulary, decorative motifs | "Geometric intersecting line ornaments at corners" | "Has decorations" |
|
||||
|
||||
**Output**: Each extracted element should be written as a **copy-pasteable prompt instruction** prefixed with "MUST" or "REQUIRED".
|
||||
@@ -0,0 +1,86 @@
|
||||
# Reference Image Handling
|
||||
|
||||
Guide for processing user-provided reference images in cover generation.
|
||||
|
||||
## Input Detection
|
||||
|
||||
| Input Type | Action |
|
||||
|------------|--------|
|
||||
| Image file path provided | Copy to `refs/` → can use `--ref` |
|
||||
| Image in conversation (no path) | **ASK user for file path** with AskUserQuestion |
|
||||
| User can't provide path | Extract style/palette verbally → append to prompt (NO frontmatter references) |
|
||||
|
||||
**CRITICAL**: Only add `references` to prompt frontmatter if files are ACTUALLY SAVED to `refs/` directory.
|
||||
|
||||
## File Saving
|
||||
|
||||
**If user provides file path**:
|
||||
1. Copy to `refs/ref-NN-{slug}.{ext}` (NN = 01, 02, ...)
|
||||
2. Create description: `refs/ref-NN-{slug}.md`
|
||||
3. Verify files exist before proceeding
|
||||
|
||||
**Description File Format**:
|
||||
```yaml
|
||||
---
|
||||
ref_id: NN
|
||||
filename: ref-NN-{slug}.{ext}
|
||||
usage: direct | style | palette
|
||||
---
|
||||
[User's description or auto-generated description]
|
||||
```
|
||||
|
||||
| Usage | When to Use |
|
||||
|-------|-------------|
|
||||
| `direct` | Reference matches desired output closely |
|
||||
| `style` | Extract visual style characteristics only |
|
||||
| `palette` | Extract color scheme only |
|
||||
|
||||
## Verbal Extraction (No File)
|
||||
|
||||
When user can't provide file path:
|
||||
1. Analyze image visually, extract: colors, style, composition
|
||||
2. Create `refs/extracted-style.md` with extracted info
|
||||
3. DO NOT add `references` to prompt frontmatter
|
||||
4. Append extracted style/colors directly to prompt text
|
||||
|
||||
## Deep Analysis ⚠️ CRITICAL
|
||||
|
||||
References are high-priority inputs. Extract **specific, concrete, reproducible** elements:
|
||||
|
||||
| Analysis | Description | Example (good vs bad) |
|
||||
|----------|-------------|----------------------|
|
||||
| **Brand elements** | Logos, wordmarks, specific typography | Good: "Logo uses vertical parallel lines for 'm'" / Bad: "Has a logo" |
|
||||
| **Signature patterns** | Unique decorative motifs, textures | Good: "Woven intersecting curves forming diamond grid" / Bad: "Has patterns" |
|
||||
| **Color palette** | Exact hex values for key colors | Good: "#2D4A3E dark teal, #F5F0E0 cream" / Bad: "Dark and light colors" |
|
||||
| **Layout structure** | Specific spatial arrangement | Good: "Bottom 30% dark banner with branding" / Bad: "Has a banner" |
|
||||
| **Typography** | Font style, weight, spacing, case | Good: "Uppercase, wide letter-spacing" / Bad: "Has text" |
|
||||
| **Content/subject** | What the reference depicts | Factual description |
|
||||
| **Usage recommendation** | `direct` / `style` / `palette` | Based on analysis |
|
||||
|
||||
**Output format**: List each element as bullet that can be copy-pasted into prompt as mandatory instruction.
|
||||
|
||||
## Verification Output
|
||||
|
||||
**For saved files**:
|
||||
```
|
||||
Reference Images Saved:
|
||||
- ref-01-{slug}.png ✓ (can use --ref)
|
||||
- ref-02-{slug}.png ✓ (can use --ref)
|
||||
```
|
||||
|
||||
**For extracted style**:
|
||||
```
|
||||
Reference Style Extracted (no file):
|
||||
- Colors: #E8756D coral, #7ECFC0 mint...
|
||||
- Style: minimal flat vector, clean lines...
|
||||
→ Will append to prompt text (not --ref)
|
||||
```
|
||||
|
||||
## Priority Rules
|
||||
|
||||
When user provides references, they are **HIGH PRIORITY**:
|
||||
|
||||
- **References override defaults**: If reference conflicts with preferred palette/rendering, reference takes precedence
|
||||
- **Concrete > abstract**: Extract specific elements — not vague "clean style"
|
||||
- **Mandatory language**: Use "MUST", "REQUIRED" in prompt for reference elements
|
||||
- **Visible in output**: Verify elements are present after generation; strengthen prompt if not
|
||||
@@ -1,6 +1,14 @@
|
||||
---
|
||||
name: baoyu-danger-gemini-web
|
||||
description: Generates images and text via reverse-engineered Gemini Web API. Supports text generation, image generation from prompts, reference images for vision input, and multi-turn conversations. Use when other skills need image generation backend, or when user requests "generate image with Gemini", "Gemini text generation", or needs vision-capable AI generation.
|
||||
version: 1.56.1
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-danger-gemini-web
|
||||
requires:
|
||||
anyBins:
|
||||
- bun
|
||||
- npx
|
||||
---
|
||||
|
||||
# Gemini Web Client
|
||||
@@ -12,9 +20,10 @@ Text/image generation via Gemini Web API. Supports reference images and multi-tu
|
||||
**Important**: All scripts are located in the `scripts/` subdirectory of this skill.
|
||||
|
||||
**Agent Execution Instructions**:
|
||||
1. Determine this SKILL.md file's directory path as `SKILL_DIR`
|
||||
2. Script path = `${SKILL_DIR}/scripts/<script-name>.ts`
|
||||
3. Replace all `${SKILL_DIR}` in this document with the actual path
|
||||
1. Determine this SKILL.md file's directory path as `{baseDir}`
|
||||
2. Script path = `{baseDir}/scripts/<script-name>.ts`
|
||||
3. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun
|
||||
4. Replace all `{baseDir}` and `${BUN_X}` in this document with actual values
|
||||
|
||||
**Script Reference**:
|
||||
| Script | Purpose |
|
||||
@@ -43,16 +52,23 @@ Before first use, verify user consent for reverse-engineered API usage.
|
||||
|
||||
## Preferences (EXTEND.md)
|
||||
|
||||
Use Bash to check EXTEND.md existence (priority order):
|
||||
Check EXTEND.md existence (priority order):
|
||||
|
||||
```bash
|
||||
# Check project-level first
|
||||
# macOS, Linux, WSL, Git Bash
|
||||
test -f .baoyu-skills/baoyu-danger-gemini-web/EXTEND.md && echo "project"
|
||||
|
||||
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-danger-gemini-web/EXTEND.md" && echo "xdg"
|
||||
test -f "$HOME/.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md" && echo "user"
|
||||
```
|
||||
|
||||
```powershell
|
||||
# PowerShell (Windows)
|
||||
if (Test-Path .baoyu-skills/baoyu-danger-gemini-web/EXTEND.md) { "project" }
|
||||
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
|
||||
if (Test-Path "$xdg/baoyu-skills/baoyu-danger-gemini-web/EXTEND.md") { "xdg" }
|
||||
if (Test-Path "$HOME/.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md") { "user" }
|
||||
```
|
||||
|
||||
┌──────────────────────────────────────────────────────────┬───────────────────┐
|
||||
│ Path │ Location │
|
||||
├──────────────────────────────────────────────────────────┼───────────────────┤
|
||||
@@ -75,23 +91,23 @@ test -f "$HOME/.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md" && echo "user"
|
||||
|
||||
```bash
|
||||
# Text generation
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts "Your prompt"
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Your prompt" --model gemini-2.5-pro
|
||||
${BUN_X} {baseDir}/scripts/main.ts "Your prompt"
|
||||
${BUN_X} {baseDir}/scripts/main.ts --prompt "Your prompt" --model gemini-3-flash
|
||||
|
||||
# Image generation
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cute cat" --image cat.png
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts --promptfiles system.md content.md --image out.png
|
||||
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cute cat" --image cat.png
|
||||
${BUN_X} {baseDir}/scripts/main.ts --promptfiles system.md content.md --image out.png
|
||||
|
||||
# Vision input (reference images)
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Describe this" --reference image.png
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Create variation" --reference a.png --image out.png
|
||||
${BUN_X} {baseDir}/scripts/main.ts --prompt "Describe this" --reference image.png
|
||||
${BUN_X} {baseDir}/scripts/main.ts --prompt "Create variation" --reference a.png --image out.png
|
||||
|
||||
# Multi-turn conversation
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts "Remember: 42" --sessionId session-abc
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts "What number?" --sessionId session-abc
|
||||
${BUN_X} {baseDir}/scripts/main.ts "Remember: 42" --sessionId session-abc
|
||||
${BUN_X} {baseDir}/scripts/main.ts "What number?" --sessionId session-abc
|
||||
|
||||
# JSON output
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello" --json
|
||||
${BUN_X} {baseDir}/scripts/main.ts "Hello" --json
|
||||
```
|
||||
|
||||
## Options
|
||||
@@ -100,7 +116,7 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello" --json
|
||||
|--------|-------------|
|
||||
| `--prompt`, `-p` | Prompt text |
|
||||
| `--promptfiles` | Read prompt from files (concatenated) |
|
||||
| `--model`, `-m` | Model: gemini-3-pro (default), gemini-2.5-pro, gemini-2.5-flash |
|
||||
| `--model`, `-m` | Model: gemini-3-pro (default), gemini-3-flash, gemini-3-flash-thinking, gemini-3.1-pro-preview |
|
||||
| `--image [path]` | Generate image (default: generated.png) |
|
||||
| `--reference`, `--ref` | Reference images for vision input |
|
||||
| `--sessionId` | Session ID for multi-turn conversation |
|
||||
@@ -114,9 +130,10 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello" --json
|
||||
|
||||
| Model | Description |
|
||||
|-------|-------------|
|
||||
| `gemini-3-pro` | Default, latest |
|
||||
| `gemini-2.5-pro` | Previous pro |
|
||||
| `gemini-2.5-flash` | Fast, lightweight |
|
||||
| `gemini-3-pro` | Default, latest 3.0 Pro |
|
||||
| `gemini-3-flash` | Fast, lightweight 3.0 Flash |
|
||||
| `gemini-3-flash-thinking` | 3.0 Flash with thinking |
|
||||
| `gemini-3.1-pro-preview` | 3.1 Pro preview (empty header, auto-routed) |
|
||||
|
||||
## Authentication
|
||||
|
||||
|
||||
@@ -47,17 +47,22 @@ export class Model {
|
||||
static readonly UNSPECIFIED = new Model('unspecified', {}, false);
|
||||
static readonly G_3_0_PRO = new Model(
|
||||
'gemini-3.0-pro',
|
||||
{ 'x-goog-ext-525001261-jspb': '[1,null,null,null,"9d8ca3786ebdfbea",null,null,0,[4]]' },
|
||||
{ 'x-goog-ext-525001261-jspb': '[1,null,null,null,"9d8ca3786ebdfbea",null,null,0,[4],null,null,1]' },
|
||||
false,
|
||||
);
|
||||
static readonly G_2_5_PRO = new Model(
|
||||
'gemini-2.5-pro',
|
||||
{ 'x-goog-ext-525001261-jspb': '[1,null,null,null,"4af6c7f5da75d65d",null,null,0,[4]]' },
|
||||
static readonly G_3_0_FLASH = new Model(
|
||||
'gemini-3.0-flash',
|
||||
{ 'x-goog-ext-525001261-jspb': '[1,null,null,null,"fbb127bbb056c959",null,null,0,[4],null,null,1]' },
|
||||
false,
|
||||
);
|
||||
static readonly G_2_5_FLASH = new Model(
|
||||
'gemini-2.5-flash',
|
||||
{ 'x-goog-ext-525001261-jspb': '[1,null,null,null,"9ec249fc9ad08861",null,null,0,[4]]' },
|
||||
static readonly G_3_0_FLASH_THINKING = new Model(
|
||||
'gemini-3.0-flash-thinking',
|
||||
{ 'x-goog-ext-525001261-jspb': '[1,null,null,null,"5bf011840784117a",null,null,0,[4],null,null,1]' },
|
||||
false,
|
||||
);
|
||||
static readonly G_3_1_PRO_PREVIEW = new Model(
|
||||
'gemini-3.1-pro-preview',
|
||||
{},
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -68,12 +73,12 @@ export class Model {
|
||||
) {}
|
||||
|
||||
static from_name(name: string): Model {
|
||||
for (const model of [Model.UNSPECIFIED, Model.G_3_0_PRO, Model.G_2_5_PRO, Model.G_2_5_FLASH]) {
|
||||
for (const model of [Model.UNSPECIFIED, Model.G_3_0_PRO, Model.G_3_0_FLASH, Model.G_3_0_FLASH_THINKING, Model.G_3_1_PRO_PREVIEW]) {
|
||||
if (model.model_name === name) return model;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Unknown model name: ${name}. Available models: ${[Model.UNSPECIFIED, Model.G_3_0_PRO, Model.G_2_5_PRO, Model.G_2_5_FLASH]
|
||||
`Unknown model name: ${name}. Available models: ${[Model.UNSPECIFIED, Model.G_3_0_PRO, Model.G_3_0_FLASH, Model.G_3_0_FLASH_THINKING, Model.G_3_1_PRO_PREVIEW]
|
||||
.map((m) => m.model_name)
|
||||
.join(', ')}`,
|
||||
);
|
||||
|
||||
@@ -91,6 +91,8 @@ class CdpConnection {
|
||||
}
|
||||
|
||||
async function get_free_port(): Promise<number> {
|
||||
const fixed = parseInt(process.env.GEMINI_WEB_DEBUG_PORT || '', 10);
|
||||
if (fixed > 0) return fixed;
|
||||
return await new Promise((resolve, reject) => {
|
||||
const srv = net.createServer();
|
||||
srv.unref();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
@@ -29,10 +30,23 @@ export function resolveGeminiWebCookiePath(): string {
|
||||
return path.join(resolveGeminiWebDataDir(), COOKIE_FILE_NAME);
|
||||
}
|
||||
|
||||
let _wslHome: string | null | undefined;
|
||||
function getWslWindowsHome(): string | null {
|
||||
if (_wslHome !== undefined) return _wslHome;
|
||||
if (!process.env.WSL_DISTRO_NAME) { _wslHome = null; return null; }
|
||||
try {
|
||||
const raw = execSync('cmd.exe /C "echo %USERPROFILE%"', { encoding: 'utf-8', timeout: 5000 }).trim().replace(/\r/g, '');
|
||||
_wslHome = execSync(`wslpath -u "${raw}"`, { encoding: 'utf-8', timeout: 5000 }).trim() || null;
|
||||
} catch { _wslHome = null; }
|
||||
return _wslHome;
|
||||
}
|
||||
|
||||
export function resolveGeminiWebChromeProfileDir(): string {
|
||||
const override = process.env.GEMINI_WEB_CHROME_PROFILE_DIR?.trim();
|
||||
const override = process.env.BAOYU_CHROME_PROFILE_DIR?.trim() || process.env.GEMINI_WEB_CHROME_PROFILE_DIR?.trim();
|
||||
if (override) return path.resolve(override);
|
||||
return path.join(resolveGeminiWebDataDir(), PROFILE_DIR_NAME);
|
||||
const wslHome = getWslWindowsHome();
|
||||
if (wslHome) return path.join(wslHome, '.local', 'share', APP_DATA_DIR, PROFILE_DIR_NAME);
|
||||
return path.join(resolveUserDataRoot(), APP_DATA_DIR, PROFILE_DIR_NAME);
|
||||
}
|
||||
|
||||
export function resolveGeminiWebSessionsDir(): string {
|
||||
|
||||
@@ -59,21 +59,36 @@ function normalizeSessionMetadata(input: unknown): Array<string | null> {
|
||||
return [null, null, null];
|
||||
}
|
||||
|
||||
function formatScriptCommand(fallback: string): string {
|
||||
const raw = process.argv[1];
|
||||
const displayPath = raw
|
||||
? (() => {
|
||||
const relative = path.relative(process.cwd(), raw);
|
||||
return relative && !relative.startsWith("..") ? relative : raw;
|
||||
})()
|
||||
: fallback;
|
||||
const quotedPath = displayPath.includes(" ")
|
||||
? `"${displayPath.replace(/"/g, '\\"')}"`
|
||||
: displayPath;
|
||||
return `npx -y bun ${quotedPath}`;
|
||||
}
|
||||
|
||||
function printUsage(cookiePath: string, profileDir: string): void {
|
||||
const cmd = formatScriptCommand("scripts/main.ts");
|
||||
console.log(`Usage:
|
||||
npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts --prompt "Hello"
|
||||
npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts "Hello"
|
||||
npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts --prompt "A cute cat" --image generated.png
|
||||
npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts --promptfiles system.md content.md --image out.png
|
||||
${cmd} --prompt "Hello"
|
||||
${cmd} "Hello"
|
||||
${cmd} --prompt "A cute cat" --image generated.png
|
||||
${cmd} --promptfiles system.md content.md --image out.png
|
||||
|
||||
Multi-turn conversation (agent generates unique sessionId):
|
||||
npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts "Remember 42" --sessionId abc123
|
||||
npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts "What number?" --sessionId abc123
|
||||
${cmd} "Remember 42" --sessionId abc123
|
||||
${cmd} "What number?" --sessionId abc123
|
||||
|
||||
Options:
|
||||
-p, --prompt <text> Prompt text
|
||||
--promptfiles <files...> Read prompt from one or more files (concatenated in order)
|
||||
-m, --model <id> gemini-3-pro | gemini-2.5-pro | gemini-2.5-flash (default: gemini-3-pro)
|
||||
-m, --model <id> gemini-3-pro | gemini-3-flash | gemini-3-flash-thinking | gemini-3.1-pro-preview (default: gemini-3-pro)
|
||||
--json Output JSON
|
||||
--image [path] Generate an image and save it (default: ./generated.png)
|
||||
--reference <files...> Reference images for vision input
|
||||
@@ -227,8 +242,11 @@ function resolveModel(id: string): Model {
|
||||
const k = id.trim();
|
||||
if (k === 'gemini-3-pro') return Model.G_3_0_PRO;
|
||||
if (k === 'gemini-3.0-pro') return Model.G_3_0_PRO;
|
||||
if (k === 'gemini-2.5-pro') return Model.G_2_5_PRO;
|
||||
if (k === 'gemini-2.5-flash') return Model.G_2_5_FLASH;
|
||||
if (k === 'gemini-3-flash') return Model.G_3_0_FLASH;
|
||||
if (k === 'gemini-3.0-flash') return Model.G_3_0_FLASH;
|
||||
if (k === 'gemini-3-flash-thinking') return Model.G_3_0_FLASH_THINKING;
|
||||
if (k === 'gemini-3.0-flash-thinking') return Model.G_3_0_FLASH_THINKING;
|
||||
if (k === 'gemini-3.1-pro-preview') return Model.G_3_1_PRO_PREVIEW;
|
||||
return Model.from_name(k);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
---
|
||||
name: baoyu-danger-x-to-markdown
|
||||
description: Converts X (Twitter) tweets and articles to markdown with YAML front matter. Uses reverse-engineered API requiring user consent. Use when user mentions "X to markdown", "tweet to markdown", "save tweet", or provides x.com/twitter.com URLs for conversion.
|
||||
version: 1.56.1
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-danger-x-to-markdown
|
||||
requires:
|
||||
anyBins:
|
||||
- bun
|
||||
- npx
|
||||
---
|
||||
|
||||
# X to Markdown
|
||||
@@ -14,8 +22,9 @@ Converts X content to markdown:
|
||||
Scripts located in `scripts/` subdirectory.
|
||||
|
||||
**Path Resolution**:
|
||||
1. `SKILL_DIR` = this SKILL.md's directory
|
||||
2. Script path = `${SKILL_DIR}/scripts/main.ts`
|
||||
1. `{baseDir}` = this SKILL.md's directory
|
||||
2. Script path = `{baseDir}/scripts/main.ts`
|
||||
3. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun
|
||||
|
||||
## Consent Requirement
|
||||
|
||||
@@ -69,16 +78,23 @@ Use `AskUserQuestion` with options: "Yes, I accept" | "No, I decline"
|
||||
|
||||
## Preferences (EXTEND.md)
|
||||
|
||||
Use Bash to check EXTEND.md existence (priority order):
|
||||
Check EXTEND.md existence (priority order):
|
||||
|
||||
```bash
|
||||
# Check project-level first
|
||||
# macOS, Linux, WSL, Git Bash
|
||||
test -f .baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md && echo "project"
|
||||
|
||||
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md" && echo "xdg"
|
||||
test -f "$HOME/.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md" && echo "user"
|
||||
```
|
||||
|
||||
```powershell
|
||||
# PowerShell (Windows)
|
||||
if (Test-Path .baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md) { "project" }
|
||||
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
|
||||
if (Test-Path "$xdg/baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md") { "xdg" }
|
||||
if (Test-Path "$HOME/.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md") { "user" }
|
||||
```
|
||||
|
||||
┌────────────────────────────────────────────────────────────┬───────────────────┐
|
||||
│ Path │ Location │
|
||||
├────────────────────────────────────────────────────────────┼───────────────────┤
|
||||
@@ -92,17 +108,53 @@ test -f "$HOME/.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md" && echo "user
|
||||
├───────────┼───────────────────────────────────────────────────────────────────────────┤
|
||||
│ Found │ Read, parse, apply settings │
|
||||
├───────────┼───────────────────────────────────────────────────────────────────────────┤
|
||||
│ Not found │ Use defaults │
|
||||
│ Not found │ **MUST** run first-time setup (see below) — do NOT silently create defaults │
|
||||
└───────────┴───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
**EXTEND.md Supports**: Default output directory | Output format preferences
|
||||
**EXTEND.md Supports**: Download media by default | Default output directory
|
||||
|
||||
### First-Time Setup (BLOCKING)
|
||||
|
||||
**CRITICAL**: When EXTEND.md is not found, you **MUST use `AskUserQuestion`** to ask the user for their preferences before creating EXTEND.md. **NEVER** create EXTEND.md with defaults without asking. This is a **BLOCKING** operation — do NOT proceed with any conversion until setup is complete.
|
||||
|
||||
Use `AskUserQuestion` with ALL questions in ONE call:
|
||||
|
||||
**Question 1** — header: "Media", question: "How to handle images and videos in tweets?"
|
||||
- "Ask each time (Recommended)" — After saving markdown, ask whether to download media
|
||||
- "Always download" — Always download media to local imgs/ and videos/ directories
|
||||
- "Never download" — Keep original remote URLs in markdown
|
||||
|
||||
**Question 2** — header: "Output", question: "Default output directory?"
|
||||
- "x-to-markdown (Recommended)" — Save to ./x-to-markdown/{username}/{tweet-id}.md
|
||||
- (User may choose "Other" to type a custom path)
|
||||
|
||||
**Question 3** — header: "Save", question: "Where to save preferences?"
|
||||
- "User (Recommended)" — ~/.baoyu-skills/ (all projects)
|
||||
- "Project" — .baoyu-skills/ (this project only)
|
||||
|
||||
After user answers, create EXTEND.md at the chosen location, confirm "Preferences saved to [path]", then continue.
|
||||
|
||||
Full reference: [references/config/first-time-setup.md](references/config/first-time-setup.md)
|
||||
|
||||
### Supported Keys
|
||||
|
||||
| Key | Default | Values | Description |
|
||||
|-----|---------|--------|-------------|
|
||||
| `download_media` | `ask` | `ask` / `1` / `0` | `ask` = prompt each time, `1` = always download, `0` = never |
|
||||
| `default_output_dir` | empty | path or empty | Default output directory (empty = `./x-to-markdown/`) |
|
||||
|
||||
**Value priority**:
|
||||
1. CLI arguments (`--download-media`, `-o`)
|
||||
2. EXTEND.md
|
||||
3. Skill defaults
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts <url>
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts <url> -o output.md
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts <url> --json
|
||||
${BUN_X} {baseDir}/scripts/main.ts <url>
|
||||
${BUN_X} {baseDir}/scripts/main.ts <url> -o output.md
|
||||
${BUN_X} {baseDir}/scripts/main.ts <url> --download-media
|
||||
${BUN_X} {baseDir}/scripts/main.ts <url> --json
|
||||
```
|
||||
|
||||
## Options
|
||||
@@ -112,6 +164,7 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts <url> --json
|
||||
| `<url>` | Tweet or article URL |
|
||||
| `-o <path>` | Output path |
|
||||
| `--json` | JSON output |
|
||||
| `--download-media` | Download image/video assets to local `imgs/` and `videos/`, and rewrite markdown links to local relative paths |
|
||||
| `--login` | Refresh cookies only |
|
||||
|
||||
## Supported URLs
|
||||
@@ -124,15 +177,42 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts <url> --json
|
||||
|
||||
```markdown
|
||||
---
|
||||
url: https://x.com/user/status/123
|
||||
url: "https://x.com/user/status/123"
|
||||
author: "Name (@user)"
|
||||
tweet_count: 3
|
||||
tweetCount: 3
|
||||
coverImage: "https://pbs.twimg.com/media/example.jpg"
|
||||
---
|
||||
|
||||
Content...
|
||||
```
|
||||
|
||||
**File structure**: `x-to-markdown/{username}/{tweet-id}.md`
|
||||
**File structure**: `x-to-markdown/{username}/{tweet-id}/{content-slug}.md`
|
||||
|
||||
When `--download-media` is enabled:
|
||||
- Images are saved to `imgs/` next to the markdown file
|
||||
- Videos are saved to `videos/` next to the markdown file
|
||||
- Markdown media links are rewritten to local relative paths
|
||||
|
||||
## Media Download Workflow
|
||||
|
||||
Based on `download_media` setting in EXTEND.md:
|
||||
|
||||
| Setting | Behavior |
|
||||
|---------|----------|
|
||||
| `1` (always) | Run script with `--download-media` flag |
|
||||
| `0` (never) | Run script without `--download-media` flag |
|
||||
| `ask` (default) | Follow the ask-each-time flow below |
|
||||
|
||||
### Ask-Each-Time Flow
|
||||
|
||||
1. Run script **without** `--download-media` → markdown saved
|
||||
2. Check saved markdown for remote media URLs (`https://` in image/video links)
|
||||
3. **If no remote media found** → done, no prompt needed
|
||||
4. **If remote media found** → use `AskUserQuestion`:
|
||||
- header: "Media", question: "Download N images/videos to local files?"
|
||||
- "Yes" — Download to local directories
|
||||
- "No" — Keep remote URLs
|
||||
5. If user confirms → run script **again** with `--download-media` (overwrites markdown with localized links)
|
||||
|
||||
## Authentication
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
name: first-time-setup
|
||||
description: First-time setup flow for baoyu-danger-x-to-markdown preferences
|
||||
---
|
||||
|
||||
# First-Time Setup
|
||||
|
||||
## Overview
|
||||
|
||||
When no EXTEND.md is found, guide user through preference setup.
|
||||
|
||||
**BLOCKING OPERATION**: This setup MUST complete before ANY other workflow steps. Do NOT:
|
||||
- Start converting tweets or articles
|
||||
- Ask about URLs or output paths
|
||||
- Proceed to any conversion
|
||||
|
||||
ONLY ask the questions in this setup flow, save EXTEND.md, then continue.
|
||||
|
||||
## Setup Flow
|
||||
|
||||
```
|
||||
No EXTEND.md found
|
||||
|
|
||||
v
|
||||
+---------------------+
|
||||
| AskUserQuestion |
|
||||
| (all questions) |
|
||||
+---------------------+
|
||||
|
|
||||
v
|
||||
+---------------------+
|
||||
| Create EXTEND.md |
|
||||
+---------------------+
|
||||
|
|
||||
v
|
||||
Continue conversion
|
||||
```
|
||||
|
||||
## Questions
|
||||
|
||||
**Language**: Use user's input language or saved language preference.
|
||||
|
||||
Use AskUserQuestion with ALL questions in ONE call:
|
||||
|
||||
### Question 1: Download Media
|
||||
|
||||
```yaml
|
||||
header: "Media"
|
||||
question: "How to handle images and videos in tweets?"
|
||||
options:
|
||||
- label: "Ask each time (Recommended)"
|
||||
description: "After saving markdown, ask whether to download media"
|
||||
- label: "Always download"
|
||||
description: "Always download media to local imgs/ and videos/ directories"
|
||||
- label: "Never download"
|
||||
description: "Keep original remote URLs in markdown"
|
||||
```
|
||||
|
||||
### Question 2: Default Output Directory
|
||||
|
||||
```yaml
|
||||
header: "Output"
|
||||
question: "Default output directory?"
|
||||
options:
|
||||
- label: "x-to-markdown (Recommended)"
|
||||
description: "Save to ./x-to-markdown/{username}/{tweet-id}.md"
|
||||
```
|
||||
|
||||
Note: User will likely choose "Other" to type a custom path.
|
||||
|
||||
### Question 3: Save Location
|
||||
|
||||
```yaml
|
||||
header: "Save"
|
||||
question: "Where to save preferences?"
|
||||
options:
|
||||
- label: "User (Recommended)"
|
||||
description: "~/.baoyu-skills/ (all projects)"
|
||||
- label: "Project"
|
||||
description: ".baoyu-skills/ (this project only)"
|
||||
```
|
||||
|
||||
## Save Locations
|
||||
|
||||
| Choice | Path | Scope |
|
||||
|--------|------|-------|
|
||||
| User | `~/.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md` | All projects |
|
||||
| Project | `.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md` | Current project |
|
||||
|
||||
## After Setup
|
||||
|
||||
1. Create directory if needed
|
||||
2. Write EXTEND.md
|
||||
3. Confirm: "Preferences saved to [path]"
|
||||
4. Continue with conversion using saved preferences
|
||||
|
||||
## EXTEND.md Template
|
||||
|
||||
```md
|
||||
download_media: [ask/1/0]
|
||||
default_output_dir: [path or empty]
|
||||
```
|
||||
|
||||
## Modifying Preferences Later
|
||||
|
||||
Users can edit EXTEND.md directly or delete it to trigger setup again.
|
||||
@@ -117,6 +117,8 @@ class CdpConnection {
|
||||
}
|
||||
|
||||
async function getFreePort(): Promise<number> {
|
||||
const fixed = parseInt(process.env.X_DEBUG_PORT || "", 10);
|
||||
if (fixed > 0) return fixed;
|
||||
return await new Promise((resolve, reject) => {
|
||||
const srv = net.createServer();
|
||||
srv.unref();
|
||||
|
||||
@@ -2,10 +2,12 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import readline from "node:readline";
|
||||
import process from "node:process";
|
||||
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
||||
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
|
||||
import { fetchXArticle } from "./graphql.js";
|
||||
import { formatArticleMarkdown } from "./markdown.js";
|
||||
import { localizeMarkdownMedia, type LocalizeMarkdownMediaResult } from "./media-localizer.js";
|
||||
import { resolveReferencedTweetsFromArticle } from "./referenced-tweets.js";
|
||||
import { hasRequiredXCookies, loadXCookies, refreshXCookies } from "./cookies.js";
|
||||
import { resolveXToMarkdownConsentPath } from "./paths.js";
|
||||
import { tweetToMarkdown } from "./tweet-to-markdown.js";
|
||||
@@ -15,6 +17,7 @@ type CliArgs = {
|
||||
output: string | null;
|
||||
json: boolean;
|
||||
login: boolean;
|
||||
downloadMedia: boolean;
|
||||
help: boolean;
|
||||
};
|
||||
|
||||
@@ -27,8 +30,22 @@ type ConsentRecord = {
|
||||
|
||||
const DISCLAIMER_VERSION = "1.0";
|
||||
|
||||
function formatScriptCommand(fallback: string): string {
|
||||
const raw = process.argv[1];
|
||||
const displayPath = raw
|
||||
? (() => {
|
||||
const relative = path.relative(process.cwd(), raw);
|
||||
return relative && !relative.startsWith("..") ? relative : raw;
|
||||
})()
|
||||
: fallback;
|
||||
const quotedPath = displayPath.includes(" ")
|
||||
? `"${displayPath.replace(/"/g, '\\"')}"`
|
||||
: displayPath;
|
||||
return `npx -y bun ${quotedPath}`;
|
||||
}
|
||||
|
||||
function printUsage(exitCode: number): never {
|
||||
const cmd = "npx -y bun skills/baoyu-danger-x-to-markdown/scripts/main.ts";
|
||||
const cmd = formatScriptCommand("scripts/main.ts");
|
||||
console.log(`X (Twitter) to Markdown
|
||||
|
||||
Usage:
|
||||
@@ -38,6 +55,7 @@ Usage:
|
||||
Options:
|
||||
--output <path>, -o Output path (file or dir). Default: ./x-to-markdown/<slug>/
|
||||
--json Output as JSON
|
||||
--download-media Download images/videos to local ./imgs and ./videos next to markdown
|
||||
--login Refresh cookies only, then exit
|
||||
--help, -h Show help
|
||||
|
||||
@@ -45,6 +63,7 @@ Examples:
|
||||
${cmd} https://x.com/username/status/1234567890
|
||||
${cmd} https://x.com/i/article/1234567890 -o ./article.md
|
||||
${cmd} https://x.com/username/status/1234567890 -o ./out/
|
||||
${cmd} https://x.com/username/status/1234567890 --download-media
|
||||
${cmd} https://x.com/username/status/1234567890 --json | jq -r '.markdownPath'
|
||||
${cmd} --login
|
||||
`);
|
||||
@@ -57,6 +76,7 @@ function parseArgs(argv: string[]): CliArgs {
|
||||
output: null,
|
||||
json: false,
|
||||
login: false,
|
||||
downloadMedia: false,
|
||||
help: false,
|
||||
};
|
||||
|
||||
@@ -80,6 +100,11 @@ function parseArgs(argv: string[]): CliArgs {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a === "--download-media") {
|
||||
out.downloadMedia = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a === "--url") {
|
||||
const v = argv[++i];
|
||||
if (!v) throw new Error("Missing value for --url");
|
||||
@@ -172,36 +197,160 @@ function sanitizeSlug(input: string): string {
|
||||
.slice(0, 120);
|
||||
}
|
||||
|
||||
function formatBackupTimestamp(date: Date = new Date()): string {
|
||||
const pad2 = (n: number) => String(n).padStart(2, "0");
|
||||
return `${date.getFullYear()}${pad2(date.getMonth() + 1)}${pad2(date.getDate())}-${pad2(date.getHours())}${pad2(
|
||||
date.getMinutes()
|
||||
)}${pad2(date.getSeconds())}`;
|
||||
function extractContentSlug(markdown: string): string {
|
||||
const headingMatch = markdown.match(/^#\s+(.+)$/m);
|
||||
if (headingMatch?.[1]) {
|
||||
return sanitizeSlug(headingMatch[1].slice(0, 60)).toLowerCase();
|
||||
}
|
||||
const lines = markdown.split("\n");
|
||||
let inFrontmatter = false;
|
||||
for (const line of lines) {
|
||||
if (line === "---") {
|
||||
inFrontmatter = !inFrontmatter;
|
||||
continue;
|
||||
}
|
||||
if (inFrontmatter) continue;
|
||||
const trimmed = line.trim();
|
||||
if (trimmed) {
|
||||
return sanitizeSlug(trimmed.slice(0, 60)).toLowerCase();
|
||||
}
|
||||
}
|
||||
return "untitled";
|
||||
}
|
||||
|
||||
async function backupDirIfExists(dir: string, log: (message: string) => void): Promise<void> {
|
||||
function resolveSlugAndId(normalizedUrl: string, kind: "tweet" | "article"): { slug: string; idPart: string } {
|
||||
const articleId = kind === "article" ? parseArticleId(normalizedUrl) : null;
|
||||
const tweetId = kind === "tweet" ? parseTweetId(normalizedUrl) : null;
|
||||
const username = kind === "tweet" ? parseTweetUsername(normalizedUrl) : null;
|
||||
|
||||
const idPart = articleId ?? tweetId ?? String(Date.now());
|
||||
const userSlug = username ? sanitizeSlug(username) : null;
|
||||
const slug = userSlug ?? idPart;
|
||||
return { slug, idPart };
|
||||
}
|
||||
|
||||
function extractFrontmatterUrls(markdown: string): string[] {
|
||||
const match = markdown.match(/^---\n([\s\S]*?)\n---/);
|
||||
if (!match?.[1]) return [];
|
||||
|
||||
const lines = match[1].split("\n");
|
||||
const urls: string[] = [];
|
||||
for (const line of lines) {
|
||||
const m = line.match(/^(url|requestedUrl):\s*["']([^"']+)["']\s*$/);
|
||||
if (m?.[2]) {
|
||||
urls.push(m[2]);
|
||||
}
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
function frontmatterMatchesTarget(
|
||||
markdown: string,
|
||||
normalizedUrl: string,
|
||||
kind: "tweet" | "article"
|
||||
): boolean {
|
||||
const urls = extractFrontmatterUrls(markdown);
|
||||
if (urls.length === 0) return false;
|
||||
|
||||
const targetId = kind === "article" ? parseArticleId(normalizedUrl) : parseTweetId(normalizedUrl);
|
||||
if (!targetId) return false;
|
||||
|
||||
for (const url of urls) {
|
||||
const candidateId = kind === "article" ? parseArticleId(url) : parseTweetId(url);
|
||||
if (candidateId && candidateId === targetId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function listMarkdownFiles(dirPath: string): string[] {
|
||||
try {
|
||||
if (!fs.existsSync(dir)) return;
|
||||
const stat = fs.statSync(dir);
|
||||
if (!stat.isDirectory()) return;
|
||||
const backup = `${dir}-backup-${formatBackupTimestamp()}`;
|
||||
await rename(dir, backup);
|
||||
log(`[x-to-markdown] Existing directory moved to: ${backup}`);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to backup existing directory (${dir}): ${error instanceof Error ? error.message : String(error ?? "")}`
|
||||
);
|
||||
return fs
|
||||
.readdirSync(dirPath)
|
||||
.filter((name) => name.toLowerCase().endsWith(".md"))
|
||||
.map((name) => path.join(dirPath, name))
|
||||
.sort();
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function resolveDefaultOutputDir(slug: string): string {
|
||||
return path.resolve(process.cwd(), "x-to-markdown", slug);
|
||||
function resolveExistingMarkdownPath(
|
||||
normalizedUrl: string,
|
||||
kind: "tweet" | "article",
|
||||
argsOutput: string | null
|
||||
): string | null {
|
||||
const { slug, idPart } = resolveSlugAndId(normalizedUrl, kind);
|
||||
const candidateDirs = new Set<string>();
|
||||
const candidateFiles = new Set<string>();
|
||||
|
||||
if (argsOutput) {
|
||||
const resolved = path.resolve(argsOutput);
|
||||
const looksDir = argsOutput.endsWith("/") || argsOutput.endsWith("\\");
|
||||
try {
|
||||
if (fs.existsSync(resolved)) {
|
||||
const stat = fs.statSync(resolved);
|
||||
if (stat.isFile()) {
|
||||
candidateFiles.add(resolved);
|
||||
} else if (stat.isDirectory()) {
|
||||
candidateDirs.add(path.join(resolved, slug, idPart));
|
||||
candidateDirs.add(resolved);
|
||||
}
|
||||
} else if (looksDir) {
|
||||
candidateDirs.add(path.join(resolved, slug, idPart));
|
||||
}
|
||||
} catch {
|
||||
// ignore and continue
|
||||
}
|
||||
} else {
|
||||
candidateDirs.add(path.resolve(process.cwd(), "x-to-markdown", slug, idPart));
|
||||
}
|
||||
|
||||
for (const filePath of candidateFiles) {
|
||||
if (!filePath.toLowerCase().endsWith(".md")) continue;
|
||||
try {
|
||||
const markdown = fs.readFileSync(filePath, "utf8");
|
||||
if (frontmatterMatchesTarget(markdown, normalizedUrl, kind)) {
|
||||
return filePath;
|
||||
}
|
||||
} catch {
|
||||
// ignore and continue
|
||||
}
|
||||
}
|
||||
|
||||
for (const dirPath of candidateDirs) {
|
||||
if (!fs.existsSync(dirPath)) continue;
|
||||
let stat: fs.Stats;
|
||||
try {
|
||||
stat = fs.statSync(dirPath);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
if (!stat.isDirectory()) continue;
|
||||
|
||||
const markdownFiles = listMarkdownFiles(dirPath);
|
||||
for (const filePath of markdownFiles) {
|
||||
try {
|
||||
const markdown = fs.readFileSync(filePath, "utf8");
|
||||
if (frontmatterMatchesTarget(markdown, normalizedUrl, kind)) {
|
||||
return filePath;
|
||||
}
|
||||
} catch {
|
||||
// ignore and continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function resolveOutputPath(
|
||||
normalizedUrl: string,
|
||||
kind: "tweet" | "article",
|
||||
argsOutput: string | null,
|
||||
contentSlug: string,
|
||||
log: (message: string) => void
|
||||
): Promise<{ outputDir: string; markdownPath: string; slug: string }> {
|
||||
const articleId = kind === "article" ? parseArticleId(normalizedUrl) : null;
|
||||
@@ -212,15 +361,14 @@ async function resolveOutputPath(
|
||||
const idPart = articleId ?? tweetId ?? String(Date.now());
|
||||
const slug = userSlug ?? idPart;
|
||||
|
||||
const defaultFileName = kind === "article" ? `${idPart}.md` : `${idPart}.md`;
|
||||
const defaultFileName = `${contentSlug}.md`;
|
||||
|
||||
if (argsOutput) {
|
||||
const wantsDir = argsOutput.endsWith("/") || argsOutput.endsWith("\\");
|
||||
const resolved = path.resolve(argsOutput);
|
||||
try {
|
||||
if (wantsDir || (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory())) {
|
||||
const outputDir = path.join(resolved, slug);
|
||||
await backupDirIfExists(outputDir, log);
|
||||
const outputDir = path.join(resolved, slug, idPart);
|
||||
await mkdir(outputDir, { recursive: true });
|
||||
return { outputDir, markdownPath: path.join(outputDir, defaultFileName), slug };
|
||||
}
|
||||
@@ -233,8 +381,7 @@ async function resolveOutputPath(
|
||||
return { outputDir, markdownPath: resolved, slug };
|
||||
}
|
||||
|
||||
const outputDir = resolveDefaultOutputDir(slug);
|
||||
await backupDirIfExists(outputDir, log);
|
||||
const outputDir = path.resolve(process.cwd(), "x-to-markdown", slug, idPart);
|
||||
await mkdir(outputDir, { recursive: true });
|
||||
return { outputDir, markdownPath: path.join(outputDir, defaultFileName), slug };
|
||||
}
|
||||
@@ -345,16 +492,18 @@ async function convertArticleToMarkdown(
|
||||
|
||||
log(`[x-to-markdown] Fetching article ${articleId}...`);
|
||||
const article = await fetchXArticle(articleId, cookieMap, false);
|
||||
const body = formatArticleMarkdown(article).trimEnd();
|
||||
const referencedTweets = await resolveReferencedTweetsFromArticle(article, cookieMap, { log });
|
||||
const { markdown: body, coverUrl } = formatArticleMarkdown(article, { referencedTweets });
|
||||
|
||||
const title = typeof (article as any)?.title === "string" ? String((article as any).title).trim() : "";
|
||||
const meta = formatMetaMarkdown({
|
||||
url: `https://x.com/i/article/${articleId}`,
|
||||
requested_url: inputUrl,
|
||||
requestedUrl: inputUrl,
|
||||
title: title || null,
|
||||
coverImage: coverUrl,
|
||||
});
|
||||
|
||||
return [meta, body].filter(Boolean).join("\n\n").trimEnd();
|
||||
return [meta, body.trimEnd()].filter(Boolean).join("\n\n").trimEnd();
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
@@ -383,13 +532,80 @@ async function main(): Promise<void> {
|
||||
}
|
||||
|
||||
const kind = articleId ? ("article" as const) : ("tweet" as const);
|
||||
const { outputDir, markdownPath, slug } = await resolveOutputPath(normalizedUrl, kind, args.output, log);
|
||||
|
||||
const markdown =
|
||||
if (args.downloadMedia) {
|
||||
const existingMarkdownPath = resolveExistingMarkdownPath(normalizedUrl, kind, args.output);
|
||||
if (existingMarkdownPath) {
|
||||
log(`[x-to-markdown] Reusing existing markdown: ${existingMarkdownPath}`);
|
||||
const existingMarkdown = await readFile(existingMarkdownPath, "utf8");
|
||||
const mediaResult = await localizeMarkdownMedia(existingMarkdown, {
|
||||
markdownPath: existingMarkdownPath,
|
||||
log,
|
||||
});
|
||||
const didLocalize =
|
||||
mediaResult.downloadedImages > 0 ||
|
||||
mediaResult.downloadedVideos > 0 ||
|
||||
mediaResult.markdown !== existingMarkdown;
|
||||
|
||||
if (didLocalize) {
|
||||
await writeFile(existingMarkdownPath, mediaResult.markdown, "utf8");
|
||||
log(
|
||||
`[x-to-markdown] Media localized: images=${mediaResult.downloadedImages}, videos=${mediaResult.downloadedVideos}`
|
||||
);
|
||||
log(`[x-to-markdown] Saved: ${existingMarkdownPath}`);
|
||||
|
||||
const { slug } = resolveSlugAndId(normalizedUrl, kind);
|
||||
if (args.json) {
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
url: articleId ? `https://x.com/i/article/${articleId}` : normalizedUrl,
|
||||
requestedUrl: normalizedUrl,
|
||||
type: kind,
|
||||
slug,
|
||||
outputDir: path.dirname(existingMarkdownPath),
|
||||
markdownPath: existingMarkdownPath,
|
||||
downloadMedia: true,
|
||||
downloadedImages: mediaResult.downloadedImages,
|
||||
downloadedVideos: mediaResult.downloadedVideos,
|
||||
imageDir: mediaResult.imageDir,
|
||||
videoDir: mediaResult.videoDir,
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
} else {
|
||||
console.log(existingMarkdownPath);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log("[x-to-markdown] Existing markdown already localized; rebuilding content to refresh placement.");
|
||||
}
|
||||
}
|
||||
|
||||
let markdown =
|
||||
kind === "article" && articleId
|
||||
? await convertArticleToMarkdown(normalizedUrl, articleId, log)
|
||||
: await tweetToMarkdown(normalizedUrl, { log });
|
||||
|
||||
const contentSlug = extractContentSlug(markdown);
|
||||
const { outputDir, markdownPath, slug } = await resolveOutputPath(normalizedUrl, kind, args.output, contentSlug, log);
|
||||
|
||||
let mediaResult: LocalizeMarkdownMediaResult | null = null;
|
||||
|
||||
if (args.downloadMedia) {
|
||||
mediaResult = await localizeMarkdownMedia(markdown, {
|
||||
markdownPath,
|
||||
log,
|
||||
});
|
||||
markdown = mediaResult.markdown;
|
||||
log(
|
||||
`[x-to-markdown] Media localized: images=${mediaResult.downloadedImages}, videos=${mediaResult.downloadedVideos}`
|
||||
);
|
||||
}
|
||||
|
||||
await writeFile(markdownPath, markdown, "utf8");
|
||||
log(`[x-to-markdown] Saved: ${markdownPath}`);
|
||||
|
||||
@@ -398,11 +614,16 @@ async function main(): Promise<void> {
|
||||
JSON.stringify(
|
||||
{
|
||||
url: articleId ? `https://x.com/i/article/${articleId}` : normalizedUrl,
|
||||
requested_url: normalizedUrl,
|
||||
requestedUrl: normalizedUrl,
|
||||
type: kind,
|
||||
slug,
|
||||
outputDir,
|
||||
markdownPath,
|
||||
downloadMedia: args.downloadMedia,
|
||||
downloadedImages: mediaResult?.downloadedImages ?? 0,
|
||||
downloadedVideos: mediaResult?.downloadedVideos ?? 0,
|
||||
imageDir: mediaResult?.imageDir ?? null,
|
||||
videoDir: mediaResult?.videoDir ?? null,
|
||||
},
|
||||
null,
|
||||
2
|
||||
|
||||
@@ -2,9 +2,22 @@ import type {
|
||||
ArticleBlock,
|
||||
ArticleContentState,
|
||||
ArticleEntity,
|
||||
ArticleEntityMapEntry,
|
||||
ArticleMediaInfo,
|
||||
} from "./types.js";
|
||||
|
||||
export type ReferencedTweetInfo = {
|
||||
id: string;
|
||||
url: string;
|
||||
authorName?: string;
|
||||
authorUsername?: string;
|
||||
text?: string;
|
||||
};
|
||||
|
||||
export type FormatArticleOptions = {
|
||||
referencedTweets?: Map<string, ReferencedTweetInfo>;
|
||||
};
|
||||
|
||||
function coerceArticleEntity(value: unknown): ArticleEntity | null {
|
||||
if (!value || typeof value !== "object") return null;
|
||||
const candidate = value as ArticleEntity;
|
||||
@@ -29,6 +42,73 @@ function normalizeCaption(caption?: string): string {
|
||||
return trimmed.replace(/\s+/g, " ");
|
||||
}
|
||||
|
||||
function summarizeTweetText(text?: string): string {
|
||||
const trimmed = text?.trim();
|
||||
if (!trimmed) return "";
|
||||
const normalized = trimmed
|
||||
.split(/\r?\n+/)
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
if (normalized.length <= 280) return normalized;
|
||||
return `${normalized.slice(0, 277)}...`;
|
||||
}
|
||||
|
||||
function buildTweetUrl(tweetId?: string, username?: string): string | null {
|
||||
if (!tweetId) return null;
|
||||
if (username) {
|
||||
return `https://x.com/${username}/status/${tweetId}`;
|
||||
}
|
||||
return `https://x.com/i/web/status/${tweetId}`;
|
||||
}
|
||||
|
||||
type EntityLookup = {
|
||||
byIndex: Map<number, ArticleEntityMapEntry>;
|
||||
byLogicalKey: Map<number, ArticleEntityMapEntry>;
|
||||
};
|
||||
|
||||
function buildEntityLookup(
|
||||
entityMap: ArticleContentState["entityMap"] | undefined
|
||||
): EntityLookup {
|
||||
const lookup: EntityLookup = {
|
||||
byIndex: new Map<number, ArticleEntityMapEntry>(),
|
||||
byLogicalKey: new Map<number, ArticleEntityMapEntry>(),
|
||||
};
|
||||
|
||||
if (!entityMap) return lookup;
|
||||
|
||||
for (const [idx, entry] of Object.entries(entityMap)) {
|
||||
const idxNum = Number(idx);
|
||||
if (Number.isFinite(idxNum)) {
|
||||
lookup.byIndex.set(idxNum, entry);
|
||||
}
|
||||
|
||||
const logicalKey = parseInt(entry?.key ?? "", 10);
|
||||
if (Number.isFinite(logicalKey) && !lookup.byLogicalKey.has(logicalKey)) {
|
||||
lookup.byLogicalKey.set(logicalKey, entry);
|
||||
}
|
||||
}
|
||||
|
||||
return lookup;
|
||||
}
|
||||
|
||||
function resolveEntityEntry(
|
||||
entityKey: number | undefined,
|
||||
entityMap: ArticleContentState["entityMap"] | undefined,
|
||||
lookup: EntityLookup
|
||||
): ArticleEntityMapEntry | undefined {
|
||||
if (entityKey === undefined) return undefined;
|
||||
|
||||
const byLogicalKey = lookup.byLogicalKey.get(entityKey);
|
||||
if (byLogicalKey) return byLogicalKey;
|
||||
|
||||
const byIndex = lookup.byIndex.get(entityKey);
|
||||
if (byIndex) return byIndex;
|
||||
|
||||
if (!entityMap) return undefined;
|
||||
return entityMap[String(entityKey)];
|
||||
}
|
||||
|
||||
function resolveMediaUrl(info?: ArticleMediaInfo): string | undefined {
|
||||
if (!info) return undefined;
|
||||
if (info.original_img_url) return info.original_img_url;
|
||||
@@ -79,11 +159,12 @@ function collectMediaUrls(
|
||||
function resolveEntityMediaLines(
|
||||
entityKey: number | undefined,
|
||||
entityMap: ArticleContentState["entityMap"] | undefined,
|
||||
entityLookup: EntityLookup,
|
||||
mediaById: Map<string, string>,
|
||||
usedUrls: Set<string>
|
||||
): string[] {
|
||||
if (entityKey === undefined || !entityMap) return [];
|
||||
const entry = entityMap[String(entityKey)];
|
||||
if (entityKey === undefined) return [];
|
||||
const entry = resolveEntityEntry(entityKey, entityMap, entityLookup);
|
||||
const value = entry?.value;
|
||||
if (!value) return [];
|
||||
const type = value.type;
|
||||
@@ -117,11 +198,142 @@ function resolveEntityMediaLines(
|
||||
return lines;
|
||||
}
|
||||
|
||||
function resolveEntityTweetLines(
|
||||
entityKey: number | undefined,
|
||||
entityMap: ArticleContentState["entityMap"] | undefined,
|
||||
entityLookup: EntityLookup,
|
||||
referencedTweets?: Map<string, ReferencedTweetInfo>
|
||||
): string[] {
|
||||
if (entityKey === undefined) return [];
|
||||
const entry = resolveEntityEntry(entityKey, entityMap, entityLookup);
|
||||
const value = entry?.value;
|
||||
if (!value || value.type !== "TWEET") return [];
|
||||
|
||||
const tweetId = typeof value.data?.tweetId === "string" ? value.data.tweetId : "";
|
||||
if (!tweetId) return [];
|
||||
|
||||
const referenced = referencedTweets?.get(tweetId);
|
||||
const url =
|
||||
referenced?.url ??
|
||||
buildTweetUrl(tweetId, referenced?.authorUsername) ??
|
||||
`https://x.com/i/web/status/${tweetId}`;
|
||||
|
||||
const authorText =
|
||||
referenced?.authorName && referenced?.authorUsername
|
||||
? `${referenced.authorName} (@${referenced.authorUsername})`
|
||||
: referenced?.authorUsername
|
||||
? `@${referenced.authorUsername}`
|
||||
: referenced?.authorName;
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push(`> 引用推文${authorText ? `:${authorText}` : ""}`);
|
||||
|
||||
const summary = summarizeTweetText(referenced?.text);
|
||||
if (summary) {
|
||||
lines.push(`> ${summary}`);
|
||||
}
|
||||
|
||||
lines.push(`> ${url}`);
|
||||
return lines;
|
||||
}
|
||||
|
||||
function buildMediaLinkMap(
|
||||
entityMap: ArticleContentState["entityMap"] | undefined
|
||||
): Map<number, string> {
|
||||
const map = new Map<number, string>();
|
||||
if (!entityMap) return map;
|
||||
|
||||
const mediaEntries: { idx: number; key: number }[] = [];
|
||||
const linkEntries: { key: number; url: string }[] = [];
|
||||
|
||||
for (const [idx, entry] of Object.entries(entityMap)) {
|
||||
const value = entry?.value;
|
||||
if (!value) continue;
|
||||
const key = parseInt(entry?.key ?? "", 10);
|
||||
if (isNaN(key)) continue;
|
||||
|
||||
if (value.type === "MEDIA" || value.type === "IMAGE") {
|
||||
mediaEntries.push({ idx: Number(idx), key });
|
||||
} else if (value.type === "LINK" && typeof value.data?.url === "string") {
|
||||
linkEntries.push({ key, url: value.data.url });
|
||||
}
|
||||
}
|
||||
|
||||
if (mediaEntries.length === 0 || linkEntries.length === 0) return map;
|
||||
|
||||
mediaEntries.sort((a, b) => a.key - b.key);
|
||||
linkEntries.sort((a, b) => a.key - b.key);
|
||||
|
||||
const pool = [...linkEntries];
|
||||
for (const media of mediaEntries) {
|
||||
if (pool.length === 0) break;
|
||||
let linkIdx = pool.findIndex((l) => l.key > media.key);
|
||||
if (linkIdx === -1) linkIdx = 0;
|
||||
const link = pool.splice(linkIdx, 1)[0]!;
|
||||
map.set(media.idx, link.url);
|
||||
map.set(media.key, link.url);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
function renderInlineLinks(
|
||||
text: string,
|
||||
entityRanges: Array<{ key?: number; offset?: number; length?: number }>,
|
||||
entityMap: ArticleContentState["entityMap"] | undefined,
|
||||
entityLookup: EntityLookup,
|
||||
mediaLinkMap: Map<number, string>
|
||||
): string {
|
||||
if (!entityMap || entityRanges.length === 0) return text;
|
||||
|
||||
const valid = entityRanges.filter(
|
||||
(r) =>
|
||||
typeof r.key === "number" &&
|
||||
typeof r.offset === "number" &&
|
||||
typeof r.length === "number" &&
|
||||
r.length > 0
|
||||
);
|
||||
if (valid.length === 0) return text;
|
||||
|
||||
const sorted = [...valid].sort((a, b) => (b.offset ?? 0) - (a.offset ?? 0));
|
||||
|
||||
let result = text;
|
||||
for (const range of sorted) {
|
||||
const offset = range.offset!;
|
||||
const length = range.length!;
|
||||
const key = range.key!;
|
||||
|
||||
const entry = resolveEntityEntry(key, entityMap, entityLookup);
|
||||
const value = entry?.value;
|
||||
if (!value) continue;
|
||||
|
||||
let url: string | undefined;
|
||||
if (value.type === "LINK" && typeof value.data?.url === "string") {
|
||||
url = value.data.url;
|
||||
} else if (value.type === "MEDIA" || value.type === "IMAGE") {
|
||||
url = mediaLinkMap.get(key);
|
||||
}
|
||||
|
||||
if (!url) continue;
|
||||
|
||||
const linkText = result.slice(offset, offset + length);
|
||||
result =
|
||||
result.slice(0, offset) +
|
||||
`[${linkText}](${url})` +
|
||||
result.slice(offset + length);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function renderContentBlocks(
|
||||
blocks: ArticleBlock[],
|
||||
entityMap: ArticleContentState["entityMap"] | undefined,
|
||||
entityLookup: EntityLookup,
|
||||
mediaById: Map<string, string>,
|
||||
usedUrls: Set<string>
|
||||
usedUrls: Set<string>,
|
||||
mediaLinkMap: Map<number, string>,
|
||||
referencedTweets?: Map<string, ReferencedTweetInfo>
|
||||
): string[] {
|
||||
const lines: string[] = [];
|
||||
let previousKind: "list" | "quote" | "heading" | "text" | "code" | "media" | null = null;
|
||||
@@ -150,14 +362,55 @@ function renderContentBlocks(
|
||||
const mediaLines: string[] = [];
|
||||
for (const range of ranges) {
|
||||
if (typeof range?.key !== "number") continue;
|
||||
mediaLines.push(...resolveEntityMediaLines(range.key, entityMap, mediaById, usedUrls));
|
||||
mediaLines.push(
|
||||
...resolveEntityMediaLines(range.key, entityMap, entityLookup, mediaById, usedUrls)
|
||||
);
|
||||
}
|
||||
return mediaLines;
|
||||
};
|
||||
|
||||
const collectTweetLines = (block: ArticleBlock): string[] => {
|
||||
const ranges = Array.isArray(block.entityRanges) ? block.entityRanges : [];
|
||||
const tweetLines: string[] = [];
|
||||
for (const range of ranges) {
|
||||
if (typeof range?.key !== "number") continue;
|
||||
tweetLines.push(
|
||||
...resolveEntityTweetLines(range.key, entityMap, entityLookup, referencedTweets)
|
||||
);
|
||||
}
|
||||
return tweetLines;
|
||||
};
|
||||
|
||||
const collectLinkLines = (block: ArticleBlock): string[] => {
|
||||
const ranges = Array.isArray(block.entityRanges) ? block.entityRanges : [];
|
||||
const linkLines: string[] = [];
|
||||
for (const range of ranges) {
|
||||
if (typeof range?.key !== "number") continue;
|
||||
const entry = resolveEntityEntry(range.key, entityMap, entityLookup);
|
||||
const value = entry?.value;
|
||||
if (value?.type !== "LINK") continue;
|
||||
const url = typeof value.data?.url === "string" ? value.data.url : "";
|
||||
if (url) {
|
||||
linkLines.push(url);
|
||||
}
|
||||
}
|
||||
return [...new Set(linkLines)];
|
||||
};
|
||||
|
||||
const pushTrailingMedia = (mediaLines: string[]) => {
|
||||
if (mediaLines.length > 0) {
|
||||
pushBlock(mediaLines, "media");
|
||||
}
|
||||
};
|
||||
|
||||
for (const block of blocks) {
|
||||
const type = typeof block?.type === "string" ? block.type : "unstyled";
|
||||
const text = typeof block?.text === "string" ? block.text : "";
|
||||
const rawText = typeof block?.text === "string" ? block.text : "";
|
||||
const ranges = Array.isArray(block.entityRanges) ? block.entityRanges : [];
|
||||
const text =
|
||||
type !== "atomic" && type !== "code-block"
|
||||
? renderInlineLinks(rawText, ranges, entityMap, entityLookup, mediaLinkMap)
|
||||
: rawText;
|
||||
|
||||
if (type === "code-block") {
|
||||
if (!inCodeBlock) {
|
||||
@@ -182,10 +435,22 @@ function renderContentBlocks(
|
||||
}
|
||||
listKind = null;
|
||||
orderedIndex = 0;
|
||||
|
||||
const tweetLines = collectTweetLines(block);
|
||||
if (tweetLines.length > 0) {
|
||||
pushBlock(tweetLines, "quote");
|
||||
}
|
||||
|
||||
const mediaLines = collectMediaLines(block);
|
||||
if (mediaLines.length > 0) {
|
||||
pushBlock(mediaLines, "media");
|
||||
}
|
||||
|
||||
const linkLines = collectLinkLines(block);
|
||||
if (linkLines.length > 0) {
|
||||
pushBlock(linkLines, "text");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -199,6 +464,7 @@ function renderContentBlocks(
|
||||
listKind = "unordered";
|
||||
orderedIndex = 0;
|
||||
pushBlock([`- ${text}`], "list");
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -209,6 +475,7 @@ function renderContentBlocks(
|
||||
listKind = "ordered";
|
||||
orderedIndex += 1;
|
||||
pushBlock([`${orderedIndex}. ${text}`], "list");
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -218,36 +485,43 @@ function renderContentBlocks(
|
||||
switch (type) {
|
||||
case "header-one":
|
||||
pushBlock([`# ${text}`], "heading");
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
break;
|
||||
case "header-two":
|
||||
pushBlock([`## ${text}`], "heading");
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
break;
|
||||
case "header-three":
|
||||
pushBlock([`### ${text}`], "heading");
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
break;
|
||||
case "header-four":
|
||||
pushBlock([`#### ${text}`], "heading");
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
break;
|
||||
case "header-five":
|
||||
pushBlock([`##### ${text}`], "heading");
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
break;
|
||||
case "header-six":
|
||||
pushBlock([`###### ${text}`], "heading");
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
break;
|
||||
case "blockquote": {
|
||||
const quoteLines = text.length > 0 ? text.split("\n") : [""];
|
||||
pushBlock(quoteLines.map((line) => `> ${line}`), "quote");
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (/^XIMGPH_\d+$/.test(text.trim())) {
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
break;
|
||||
}
|
||||
pushBlock([text], "text");
|
||||
pushTrailingMedia(collectMediaLines(block));
|
||||
break;
|
||||
}
|
||||
|
||||
const trailingMediaLines = collectMediaLines(block);
|
||||
if (trailingMediaLines.length > 0) {
|
||||
pushBlock(trailingMediaLines, "media");
|
||||
}
|
||||
}
|
||||
|
||||
if (inCodeBlock) {
|
||||
@@ -257,10 +531,36 @@ function renderContentBlocks(
|
||||
return lines;
|
||||
}
|
||||
|
||||
export function formatArticleMarkdown(article: unknown): string {
|
||||
export type FormatArticleResult = {
|
||||
markdown: string;
|
||||
coverUrl: string | null;
|
||||
};
|
||||
|
||||
export function extractReferencedTweetIds(article: unknown): string[] {
|
||||
const candidate = coerceArticleEntity(article);
|
||||
const entityMap = candidate?.content_state?.entityMap;
|
||||
if (!entityMap) return [];
|
||||
|
||||
const ids: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const entry of Object.values(entityMap)) {
|
||||
const value = entry?.value;
|
||||
if (value?.type !== "TWEET") continue;
|
||||
const tweetId = typeof value.data?.tweetId === "string" ? value.data.tweetId : "";
|
||||
if (!tweetId || seen.has(tweetId)) continue;
|
||||
seen.add(tweetId);
|
||||
ids.push(tweetId);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
export function formatArticleMarkdown(
|
||||
article: unknown,
|
||||
options: FormatArticleOptions = {}
|
||||
): FormatArticleResult {
|
||||
const candidate = coerceArticleEntity(article);
|
||||
if (!candidate) {
|
||||
return `\`\`\`json\n${JSON.stringify(article, null, 2)}\n\`\`\``;
|
||||
return { markdown: `\`\`\`json\n${JSON.stringify(article, null, 2)}\n\`\`\``, coverUrl: null };
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
@@ -271,17 +571,25 @@ export function formatArticleMarkdown(article: unknown): string {
|
||||
lines.push(`# ${title}`);
|
||||
}
|
||||
|
||||
const coverUrl = resolveMediaUrl(candidate.cover_media?.media_info);
|
||||
const coverUrl = resolveMediaUrl(candidate.cover_media?.media_info) ?? null;
|
||||
if (coverUrl) {
|
||||
if (lines.length > 0) lines.push("");
|
||||
lines.push(``);
|
||||
usedUrls.add(coverUrl);
|
||||
}
|
||||
|
||||
const blocks = candidate.content_state?.blocks;
|
||||
const entityMap = candidate.content_state?.entityMap;
|
||||
const entityLookup = buildEntityLookup(entityMap);
|
||||
if (Array.isArray(blocks) && blocks.length > 0) {
|
||||
const rendered = renderContentBlocks(blocks, entityMap, mediaById, usedUrls);
|
||||
const mediaLinkMap = buildMediaLinkMap(entityMap);
|
||||
const rendered = renderContentBlocks(
|
||||
blocks,
|
||||
entityMap,
|
||||
entityLookup,
|
||||
mediaById,
|
||||
usedUrls,
|
||||
mediaLinkMap,
|
||||
options.referencedTweets
|
||||
);
|
||||
if (rendered.length > 0) {
|
||||
if (lines.length > 0) lines.push("");
|
||||
lines.push(...rendered);
|
||||
@@ -294,7 +602,7 @@ export function formatArticleMarkdown(article: unknown): string {
|
||||
lines.push(candidate.preview_text.trim());
|
||||
}
|
||||
|
||||
const mediaUrls = collectMediaUrls(candidate, usedUrls, coverUrl);
|
||||
const mediaUrls = collectMediaUrls(candidate, usedUrls, coverUrl ?? undefined);
|
||||
if (mediaUrls.length > 0) {
|
||||
lines.push("", "## Media", "");
|
||||
for (const url of mediaUrls) {
|
||||
@@ -302,5 +610,5 @@ export function formatArticleMarkdown(article: unknown): string {
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join("\n").trimEnd();
|
||||
return { markdown: lines.join("\n").trimEnd(), coverUrl };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
import path from "node:path";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
|
||||
type MediaKind = "image" | "video";
|
||||
type MediaHint = "image" | "unknown";
|
||||
|
||||
type MarkdownLinkCandidate = {
|
||||
url: string;
|
||||
hint: MediaHint;
|
||||
};
|
||||
|
||||
export type LocalizeMarkdownMediaOptions = {
|
||||
markdownPath: string;
|
||||
log?: (message: string) => void;
|
||||
};
|
||||
|
||||
export type LocalizeMarkdownMediaResult = {
|
||||
markdown: string;
|
||||
downloadedImages: number;
|
||||
downloadedVideos: number;
|
||||
imageDir: string | null;
|
||||
videoDir: string | null;
|
||||
};
|
||||
|
||||
const MARKDOWN_LINK_RE = /(!?\[[^\]\n]*\])\((<)?(https?:\/\/[^)\s>]+)(>)?\)/g;
|
||||
|
||||
const IMAGE_EXTENSIONS = new Set([
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"png",
|
||||
"webp",
|
||||
"gif",
|
||||
"bmp",
|
||||
"avif",
|
||||
"heic",
|
||||
"heif",
|
||||
"svg",
|
||||
]);
|
||||
|
||||
const VIDEO_EXTENSIONS = new Set(["mp4", "m4v", "mov", "webm", "mkv"]);
|
||||
|
||||
const MIME_EXTENSION_MAP: Record<string, string> = {
|
||||
"image/jpeg": "jpg",
|
||||
"image/jpg": "jpg",
|
||||
"image/png": "png",
|
||||
"image/webp": "webp",
|
||||
"image/gif": "gif",
|
||||
"image/bmp": "bmp",
|
||||
"image/avif": "avif",
|
||||
"image/heic": "heic",
|
||||
"image/heif": "heif",
|
||||
"image/svg+xml": "svg",
|
||||
"video/mp4": "mp4",
|
||||
"video/webm": "webm",
|
||||
"video/quicktime": "mov",
|
||||
"video/x-m4v": "m4v",
|
||||
};
|
||||
|
||||
const DOWNLOAD_USER_AGENT =
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36";
|
||||
|
||||
function normalizeContentType(raw: string | null): string {
|
||||
return raw?.split(";")[0]?.trim().toLowerCase() ?? "";
|
||||
}
|
||||
|
||||
function normalizeExtension(raw: string | undefined | null): string | undefined {
|
||||
if (!raw) return undefined;
|
||||
const trimmed = raw.replace(/^\./, "").trim().toLowerCase();
|
||||
if (!trimmed) return undefined;
|
||||
if (trimmed === "jpeg") return "jpg";
|
||||
if (trimmed === "jpg") return "jpg";
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function resolveExtensionFromUrl(rawUrl: string): string | undefined {
|
||||
try {
|
||||
const parsed = new URL(rawUrl);
|
||||
const extFromPath = normalizeExtension(path.posix.extname(parsed.pathname));
|
||||
if (extFromPath) return extFromPath;
|
||||
const extFromFormat = normalizeExtension(parsed.searchParams.get("format"));
|
||||
if (extFromFormat) return extFromFormat;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveKindFromContentType(contentType: string): MediaKind | undefined {
|
||||
if (!contentType) return undefined;
|
||||
if (contentType.startsWith("image/")) return "image";
|
||||
if (contentType.startsWith("video/")) return "video";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveKindFromExtension(ext: string | undefined): MediaKind | undefined {
|
||||
if (!ext) return undefined;
|
||||
if (IMAGE_EXTENSIONS.has(ext)) return "image";
|
||||
if (VIDEO_EXTENSIONS.has(ext)) return "video";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveKindFromHostname(rawUrl: string): MediaKind | undefined {
|
||||
try {
|
||||
const hostname = new URL(rawUrl).hostname.toLowerCase();
|
||||
if (hostname.includes("video.twimg.com")) return "video";
|
||||
if (hostname.includes("pbs.twimg.com")) return "image";
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveMediaKind(
|
||||
rawUrl: string,
|
||||
contentType: string,
|
||||
extension: string | undefined,
|
||||
hint: MediaHint
|
||||
): MediaKind | undefined {
|
||||
const kindFromType = resolveKindFromContentType(contentType);
|
||||
if (kindFromType) return kindFromType;
|
||||
|
||||
const kindFromExtension = resolveKindFromExtension(extension);
|
||||
if (kindFromExtension) return kindFromExtension;
|
||||
|
||||
const kindFromHost = resolveKindFromHostname(rawUrl);
|
||||
if (kindFromHost) return kindFromHost;
|
||||
|
||||
if (contentType && contentType !== "application/octet-stream") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return hint === "image" ? "image" : undefined;
|
||||
}
|
||||
|
||||
function resolveOutputExtension(
|
||||
contentType: string,
|
||||
extension: string | undefined,
|
||||
kind: MediaKind
|
||||
): string {
|
||||
const extFromMime = normalizeExtension(MIME_EXTENSION_MAP[contentType]);
|
||||
if (extFromMime) return extFromMime;
|
||||
|
||||
const normalizedExt = normalizeExtension(extension);
|
||||
if (normalizedExt) return normalizedExt;
|
||||
|
||||
return kind === "video" ? "mp4" : "jpg";
|
||||
}
|
||||
|
||||
function safeDecodeURIComponent(value: string): string {
|
||||
try {
|
||||
return decodeURIComponent(value);
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeFileSegment(input: string): string {
|
||||
return input
|
||||
.replace(/[^a-zA-Z0-9_-]+/g, "-")
|
||||
.replace(/-+/g, "-")
|
||||
.replace(/^[-_]+|[-_]+$/g, "")
|
||||
.slice(0, 48);
|
||||
}
|
||||
|
||||
function resolveFileStem(rawUrl: string, extension: string): string {
|
||||
try {
|
||||
const parsed = new URL(rawUrl);
|
||||
const base = path.posix.basename(parsed.pathname);
|
||||
if (!base) return "";
|
||||
const decodedBase = safeDecodeURIComponent(base);
|
||||
const normalizedExt = normalizeExtension(extension);
|
||||
const stripExt = normalizedExt ? new RegExp(`\\.${normalizedExt}$`, "i") : null;
|
||||
const rawStem = stripExt ? decodedBase.replace(stripExt, "") : decodedBase;
|
||||
return sanitizeFileSegment(rawStem);
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function buildFileName(kind: MediaKind, index: number, sourceUrl: string, extension: string): string {
|
||||
const stem = resolveFileStem(sourceUrl, extension);
|
||||
const prefix = kind === "image" ? "img" : "video";
|
||||
const serial = String(index).padStart(3, "0");
|
||||
const suffix = stem ? `-${stem}` : "";
|
||||
return `${prefix}-${serial}${suffix}.${extension}`;
|
||||
}
|
||||
|
||||
const FRONTMATTER_COVER_RE = /^(coverImage:\s*")(https?:\/\/[^"]+)(")/m;
|
||||
|
||||
function toHighResUrl(rawUrl: string): string {
|
||||
try {
|
||||
const parsed = new URL(rawUrl);
|
||||
if (parsed.hostname !== "pbs.twimg.com") return rawUrl;
|
||||
const ext = path.posix.extname(parsed.pathname).replace(/^\./, "").toLowerCase();
|
||||
if (!ext || !IMAGE_EXTENSIONS.has(ext)) return rawUrl;
|
||||
parsed.pathname = parsed.pathname.replace(new RegExp(`\\.${ext}$`), "");
|
||||
parsed.searchParams.set("format", ext === "jpeg" ? "jpg" : ext);
|
||||
parsed.searchParams.set("name", "4096x4096");
|
||||
return parsed.toString();
|
||||
} catch {
|
||||
return rawUrl;
|
||||
}
|
||||
}
|
||||
|
||||
function collectMarkdownLinkCandidates(markdown: string): MarkdownLinkCandidate[] {
|
||||
const candidates: MarkdownLinkCandidate[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
|
||||
if (fmMatch) {
|
||||
const coverMatch = fmMatch[1]?.match(FRONTMATTER_COVER_RE);
|
||||
if (coverMatch?.[2] && !seen.has(coverMatch[2])) {
|
||||
seen.add(coverMatch[2]);
|
||||
candidates.push({ url: coverMatch[2], hint: "image" });
|
||||
}
|
||||
}
|
||||
|
||||
MARKDOWN_LINK_RE.lastIndex = 0;
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = MARKDOWN_LINK_RE.exec(markdown))) {
|
||||
const label = match[1] ?? "";
|
||||
const rawUrl = match[3] ?? "";
|
||||
if (!rawUrl || seen.has(rawUrl)) continue;
|
||||
seen.add(rawUrl);
|
||||
candidates.push({
|
||||
url: rawUrl,
|
||||
hint: label.startsWith("![") ? "image" : "unknown",
|
||||
});
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
function rewriteMarkdownMediaLinks(markdown: string, replacements: Map<string, string>): string {
|
||||
if (replacements.size === 0) return markdown;
|
||||
MARKDOWN_LINK_RE.lastIndex = 0;
|
||||
|
||||
let result = markdown.replace(MARKDOWN_LINK_RE, (full, label, _openAngle, rawUrl) => {
|
||||
const localPath = replacements.get(rawUrl);
|
||||
if (!localPath) return full;
|
||||
return `${label}(${localPath})`;
|
||||
});
|
||||
|
||||
result = result.replace(FRONTMATTER_COVER_RE, (full, prefix, rawUrl, suffix) => {
|
||||
const localPath = replacements.get(rawUrl);
|
||||
if (!localPath) return full;
|
||||
return `${prefix}${localPath}${suffix}`;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function localizeMarkdownMedia(
|
||||
markdown: string,
|
||||
options: LocalizeMarkdownMediaOptions
|
||||
): Promise<LocalizeMarkdownMediaResult> {
|
||||
const log = options.log ?? (() => {});
|
||||
const markdownDir = path.dirname(options.markdownPath);
|
||||
const candidates = collectMarkdownLinkCandidates(markdown);
|
||||
|
||||
if (candidates.length === 0) {
|
||||
return {
|
||||
markdown,
|
||||
downloadedImages: 0,
|
||||
downloadedVideos: 0,
|
||||
imageDir: null,
|
||||
videoDir: null,
|
||||
};
|
||||
}
|
||||
|
||||
const replacements = new Map<string, string>();
|
||||
let downloadedImages = 0;
|
||||
let downloadedVideos = 0;
|
||||
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
const downloadUrl = toHighResUrl(candidate.url);
|
||||
const response = await fetch(downloadUrl, {
|
||||
method: "GET",
|
||||
redirect: "follow",
|
||||
headers: {
|
||||
"user-agent": DOWNLOAD_USER_AGENT,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
log(`[x-to-markdown] Skip media (${response.status}): ${candidate.url}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const sourceUrl = response.url || candidate.url;
|
||||
const contentType = normalizeContentType(response.headers.get("content-type"));
|
||||
const extension = resolveExtensionFromUrl(sourceUrl) ?? resolveExtensionFromUrl(candidate.url);
|
||||
const kind = resolveMediaKind(sourceUrl, contentType, extension, candidate.hint);
|
||||
if (!kind) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const outputExtension = resolveOutputExtension(contentType, extension, kind);
|
||||
const nextIndex = kind === "image" ? downloadedImages + 1 : downloadedVideos + 1;
|
||||
const dirName = kind === "image" ? "imgs" : "videos";
|
||||
const targetDir = path.join(markdownDir, dirName);
|
||||
await mkdir(targetDir, { recursive: true });
|
||||
|
||||
const fileName = buildFileName(kind, nextIndex, sourceUrl, outputExtension);
|
||||
const absolutePath = path.join(targetDir, fileName);
|
||||
const relativePath = path.posix.join(dirName, fileName);
|
||||
const bytes = Buffer.from(await response.arrayBuffer());
|
||||
await writeFile(absolutePath, bytes);
|
||||
replacements.set(candidate.url, relativePath);
|
||||
|
||||
if (kind === "image") {
|
||||
downloadedImages = nextIndex;
|
||||
} else {
|
||||
downloadedVideos = nextIndex;
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error ?? "");
|
||||
log(`[x-to-markdown] Failed to download media ${candidate.url}: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
markdown: rewriteMarkdownMediaLinks(markdown, replacements),
|
||||
downloadedImages,
|
||||
downloadedVideos,
|
||||
imageDir: downloadedImages > 0 ? path.join(markdownDir, "imgs") : null,
|
||||
videoDir: downloadedVideos > 0 ? path.join(markdownDir, "videos") : null,
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
@@ -30,10 +31,23 @@ export function resolveXToMarkdownCookiePath(): string {
|
||||
return path.join(resolveXToMarkdownDataDir(), COOKIE_FILE_NAME);
|
||||
}
|
||||
|
||||
let _wslHome: string | null | undefined;
|
||||
function getWslWindowsHome(): string | null {
|
||||
if (_wslHome !== undefined) return _wslHome;
|
||||
if (!process.env.WSL_DISTRO_NAME) { _wslHome = null; return null; }
|
||||
try {
|
||||
const raw = execSync('cmd.exe /C "echo %USERPROFILE%"', { encoding: 'utf-8', timeout: 5000 }).trim().replace(/\r/g, '');
|
||||
_wslHome = execSync(`wslpath -u "${raw}"`, { encoding: 'utf-8', timeout: 5000 }).trim() || null;
|
||||
} catch { _wslHome = null; }
|
||||
return _wslHome;
|
||||
}
|
||||
|
||||
export function resolveXToMarkdownChromeProfileDir(): string {
|
||||
const override = process.env.X_CHROME_PROFILE_DIR?.trim();
|
||||
const override = process.env.BAOYU_CHROME_PROFILE_DIR?.trim() || process.env.X_CHROME_PROFILE_DIR?.trim();
|
||||
if (override) return path.resolve(override);
|
||||
return path.join(resolveXToMarkdownDataDir(), PROFILE_DIR_NAME);
|
||||
const wslHome = getWslWindowsHome();
|
||||
if (wslHome) return path.join(wslHome, ".local", "share", APP_DATA_DIR, PROFILE_DIR_NAME);
|
||||
return path.join(resolveUserDataRoot(), APP_DATA_DIR, PROFILE_DIR_NAME);
|
||||
}
|
||||
|
||||
export function resolveXToMarkdownConsentPath(): string {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { fetchXTweet } from "./graphql.js";
|
||||
import {
|
||||
extractReferencedTweetIds,
|
||||
type ReferencedTweetInfo,
|
||||
} from "./markdown.js";
|
||||
|
||||
type ResolveReferencedTweetsOptions = {
|
||||
log?: (message: string) => void;
|
||||
};
|
||||
|
||||
function extractReferencedTweetInfo(tweet: any, fallbackTweetId: string): ReferencedTweetInfo {
|
||||
const userCore = tweet?.core?.user_results?.result?.core;
|
||||
const userLegacy = tweet?.core?.user_results?.result?.legacy;
|
||||
|
||||
const authorName =
|
||||
typeof userCore?.name === "string"
|
||||
? userCore.name
|
||||
: typeof userLegacy?.name === "string"
|
||||
? userLegacy.name
|
||||
: undefined;
|
||||
|
||||
const authorUsername =
|
||||
typeof userCore?.screen_name === "string"
|
||||
? userCore.screen_name
|
||||
: typeof userLegacy?.screen_name === "string"
|
||||
? userLegacy.screen_name
|
||||
: undefined;
|
||||
|
||||
const text =
|
||||
tweet?.note_tweet?.note_tweet_results?.result?.text ??
|
||||
tweet?.legacy?.full_text ??
|
||||
tweet?.legacy?.text ??
|
||||
undefined;
|
||||
|
||||
const tweetId =
|
||||
typeof tweet?.rest_id === "string" && tweet.rest_id.length > 0
|
||||
? tweet.rest_id
|
||||
: fallbackTweetId;
|
||||
|
||||
const url = authorUsername
|
||||
? `https://x.com/${authorUsername}/status/${tweetId}`
|
||||
: `https://x.com/i/web/status/${tweetId}`;
|
||||
|
||||
return {
|
||||
id: tweetId,
|
||||
url,
|
||||
authorName,
|
||||
authorUsername,
|
||||
text: typeof text === "string" ? text : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolveReferencedTweetsFromArticle(
|
||||
article: unknown,
|
||||
cookieMap: Record<string, string>,
|
||||
options: ResolveReferencedTweetsOptions = {}
|
||||
): Promise<Map<string, ReferencedTweetInfo>> {
|
||||
const log = options.log ?? (() => {});
|
||||
const ids = extractReferencedTweetIds(article);
|
||||
const referencedTweets = new Map<string, ReferencedTweetInfo>();
|
||||
|
||||
for (const id of ids) {
|
||||
try {
|
||||
const tweet = await fetchXTweet(id, cookieMap, false);
|
||||
const info = extractReferencedTweetInfo(tweet, id);
|
||||
referencedTweets.set(id, info);
|
||||
} catch (error) {
|
||||
log(
|
||||
`[x-to-markdown] Failed to fetch referenced tweet ${id}: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`
|
||||
);
|
||||
referencedTweets.set(id, {
|
||||
id,
|
||||
url: `https://x.com/i/web/status/${id}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return referencedTweets;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { fileURLToPath } from "node:url";
|
||||
import { hasRequiredXCookies, loadXCookies } from "./cookies.js";
|
||||
import { fetchTweetThread } from "./thread.js";
|
||||
import { formatArticleMarkdown } from "./markdown.js";
|
||||
import { resolveReferencedTweetsFromArticle } from "./referenced-tweets.js";
|
||||
import { formatThreadTweetsMarkdown } from "./thread-markdown.js";
|
||||
import { resolveArticleEntityFromTweet } from "./tweet-article.js";
|
||||
|
||||
@@ -35,6 +36,20 @@ function normalizeInputUrl(input: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
function formatScriptCommand(fallback: string): string {
|
||||
const raw = process.argv[1];
|
||||
const displayPath = raw
|
||||
? (() => {
|
||||
const relative = path.relative(process.cwd(), raw);
|
||||
return relative && !relative.startsWith("..") ? relative : raw;
|
||||
})()
|
||||
: fallback;
|
||||
const quotedPath = displayPath.includes(" ")
|
||||
? `"${displayPath.replace(/"/g, '\\"')}"`
|
||||
: displayPath;
|
||||
return `npx -y bun ${quotedPath}`;
|
||||
}
|
||||
|
||||
function parseTweetId(input: string): string | null {
|
||||
const trimmed = input.trim();
|
||||
if (!trimmed) return null;
|
||||
@@ -123,22 +138,16 @@ export async function tweetToMarkdown(
|
||||
const requestedUrl = normalizedUrl || buildTweetUrl(username, tweetId) || inputUrl.trim();
|
||||
const rootUrl = buildTweetUrl(username, thread.rootId ?? tweetId) ?? requestedUrl;
|
||||
|
||||
const meta = formatMetaMarkdown({
|
||||
url: rootUrl,
|
||||
requested_url: requestedUrl,
|
||||
author,
|
||||
author_name: name ?? null,
|
||||
author_username: username ?? null,
|
||||
author_url: authorUrl ?? null,
|
||||
tweet_count: thread.totalTweets ?? tweets.length,
|
||||
});
|
||||
|
||||
const parts: string[] = [meta];
|
||||
|
||||
const articleEntity = await resolveArticleEntityFromTweet(firstTweet, cookieMap);
|
||||
let coverImage: string | null = null;
|
||||
let remainingTweets = tweets;
|
||||
const parts: string[] = [];
|
||||
|
||||
if (articleEntity) {
|
||||
const articleMarkdown = formatArticleMarkdown(articleEntity).trimEnd();
|
||||
const referencedTweets = await resolveReferencedTweetsFromArticle(articleEntity, cookieMap, { log });
|
||||
const articleResult = formatArticleMarkdown(articleEntity, { referencedTweets });
|
||||
coverImage = articleResult.coverUrl;
|
||||
const articleMarkdown = articleResult.markdown.trimEnd();
|
||||
if (articleMarkdown) {
|
||||
parts.push(articleMarkdown);
|
||||
const firstTweetText = extractTweetText(firstTweet);
|
||||
@@ -148,6 +157,19 @@ export async function tweetToMarkdown(
|
||||
}
|
||||
}
|
||||
|
||||
const meta = formatMetaMarkdown({
|
||||
url: rootUrl,
|
||||
requestedUrl: requestedUrl,
|
||||
author,
|
||||
authorName: name ?? null,
|
||||
authorUsername: username ?? null,
|
||||
authorUrl: authorUrl ?? null,
|
||||
tweetCount: thread.totalTweets ?? tweets.length,
|
||||
coverImage,
|
||||
});
|
||||
|
||||
parts.unshift(meta);
|
||||
|
||||
if (remainingTweets.length > 0) {
|
||||
const hasArticle = parts.length > 1;
|
||||
if (hasArticle) {
|
||||
@@ -171,7 +193,7 @@ async function main() {
|
||||
const { url } = parseArgs();
|
||||
if (!url) {
|
||||
console.error("Usage:");
|
||||
console.error(" npx -y bun skills/baoyu-danger-x-to-markdown/scripts/tweet-to-markdown.ts <tweet url>");
|
||||
console.error(` ${formatScriptCommand("scripts/tweet-to-markdown.ts")} <tweet url>`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ export type ArticleEntityMapEntry = {
|
||||
caption?: string;
|
||||
mediaItems?: ArticleEntityMapMediaItem[];
|
||||
url?: string;
|
||||
tweetId?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
---
|
||||
name: baoyu-format-markdown
|
||||
description: Formats plain text or markdown files with frontmatter, titles, summaries, headings, bold, lists, and code blocks. Use when user asks to "format markdown", "beautify article", "add formatting", or improve article layout. Outputs to {filename}-formatted.md.
|
||||
version: 1.56.1
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-format-markdown
|
||||
requires:
|
||||
anyBins:
|
||||
- bun
|
||||
- npx
|
||||
---
|
||||
|
||||
# Markdown Formatter
|
||||
|
||||
Transforms plain text or markdown into well-structured, reader-friendly markdown. The goal is to help readers quickly grasp key points, highlights, and structure — without changing any original content.
|
||||
|
||||
**Core principle**: Only adjust formatting and fix obvious typos. Never add, delete, or rewrite content.
|
||||
|
||||
## Script Directory
|
||||
|
||||
Scripts in `scripts/` subdirectory. `{baseDir}` = this SKILL.md's directory path. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun. Replace `{baseDir}` and `${BUN_X}` with actual values.
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `scripts/main.ts` | Main entry point with CLI options (uses remark-cjk-friendly for CJK emphasis) |
|
||||
| `scripts/quotes.ts` | Replace ASCII quotes with fullwidth quotes |
|
||||
| `scripts/autocorrect.ts` | Add CJK/English spacing via autocorrect |
|
||||
|
||||
## Preferences (EXTEND.md)
|
||||
|
||||
Check EXTEND.md existence (priority order):
|
||||
|
||||
```bash
|
||||
# macOS, Linux, WSL, Git Bash
|
||||
test -f .baoyu-skills/baoyu-format-markdown/EXTEND.md && echo "project"
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-format-markdown/EXTEND.md" && echo "xdg"
|
||||
test -f "$HOME/.baoyu-skills/baoyu-format-markdown/EXTEND.md" && echo "user"
|
||||
```
|
||||
|
||||
```powershell
|
||||
# PowerShell (Windows)
|
||||
if (Test-Path .baoyu-skills/baoyu-format-markdown/EXTEND.md) { "project" }
|
||||
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
|
||||
if (Test-Path "$xdg/baoyu-skills/baoyu-format-markdown/EXTEND.md") { "xdg" }
|
||||
if (Test-Path "$HOME/.baoyu-skills/baoyu-format-markdown/EXTEND.md") { "user" }
|
||||
```
|
||||
|
||||
┌──────────────────────────────────────────────────────────┬───────────────────┐
|
||||
│ Path │ Location │
|
||||
├──────────────────────────────────────────────────────────┼───────────────────┤
|
||||
│ .baoyu-skills/baoyu-format-markdown/EXTEND.md │ Project directory │
|
||||
├──────────────────────────────────────────────────────────┼───────────────────┤
|
||||
│ $HOME/.baoyu-skills/baoyu-format-markdown/EXTEND.md │ User home │
|
||||
└──────────────────────────────────────────────────────────┴───────────────────┘
|
||||
|
||||
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
|
||||
│ Result │ Action │
|
||||
├───────────┼───────────────────────────────────────────────────────────────────────────┤
|
||||
│ Found │ Read, parse, apply settings │
|
||||
├───────────┼───────────────────────────────────────────────────────────────────────────┤
|
||||
│ Not found │ Use defaults │
|
||||
└───────────┴───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
**EXTEND.md Supports**:
|
||||
|
||||
| Setting | Values | Default | Description |
|
||||
|---------|--------|---------|-------------|
|
||||
| `auto_select` | `true`/`false` | `false` | Skip both title and summary selection, auto-pick best |
|
||||
| `auto_select_title` | `true`/`false` | `false` | Skip title selection only |
|
||||
| `auto_select_summary` | `true`/`false` | `false` | Skip summary selection only |
|
||||
| Other | — | — | Default formatting options, typography preferences |
|
||||
|
||||
## Usage
|
||||
|
||||
The workflow has two phases: **Analyze** (understand the content) then **Format** (apply formatting). Claude performs content analysis and formatting (Steps 1-5), then runs the script for typography fixes (Step 6).
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Read & Detect Content Type
|
||||
|
||||
Read the user-specified file, then detect content type:
|
||||
|
||||
| Indicator | Classification |
|
||||
|-----------|----------------|
|
||||
| Has `---` YAML frontmatter | Markdown |
|
||||
| Has `#`, `##`, `###` headings | Markdown |
|
||||
| Has `**bold**`, `*italic*`, lists, code blocks, blockquotes | Markdown |
|
||||
| None of above | Plain text |
|
||||
|
||||
**If Markdown detected, ask user:**
|
||||
|
||||
```
|
||||
Detected existing markdown formatting. What would you like to do?
|
||||
|
||||
1. Optimize formatting (Recommended)
|
||||
- Analyze content, improve headings, bold, lists for readability
|
||||
- Run typography script (spacing, emphasis fixes)
|
||||
- Output: {filename}-formatted.md
|
||||
|
||||
2. Keep original formatting
|
||||
- Preserve existing markdown structure
|
||||
- Run typography script only
|
||||
- Output: {filename}-formatted.md
|
||||
|
||||
3. Typography fixes only
|
||||
- Run typography script on original file in-place
|
||||
- No copy created, modifies original file directly
|
||||
```
|
||||
|
||||
**Based on user choice:**
|
||||
- **Optimize**: Continue to Step 2 (full workflow)
|
||||
- **Keep original**: Skip to Step 5, copy file then run Step 6
|
||||
- **Typography only**: Skip to Step 6, run on original file directly
|
||||
|
||||
### Step 2: Analyze Content (Reader's Perspective)
|
||||
|
||||
Read the entire content carefully. Think from a reader's perspective: what would help them quickly understand and remember the key information?
|
||||
|
||||
Produce an analysis covering these dimensions:
|
||||
|
||||
**2.1 Highlights & Key Insights**
|
||||
- Core arguments or conclusions the author makes
|
||||
- Surprising facts, data points, or counterintuitive claims
|
||||
- Memorable quotes or well-phrased sentences (golden quotes)
|
||||
|
||||
**2.2 Structure Assessment**
|
||||
- Does the content have a clear logical flow? What is it?
|
||||
- Are there natural section boundaries that lack headings?
|
||||
- Are there long walls of text that could benefit from visual breaks?
|
||||
|
||||
**2.3 Reader-Important Information**
|
||||
- Actionable advice or takeaways
|
||||
- Definitions, explanations of key concepts
|
||||
- Lists or enumerations buried in prose
|
||||
- Comparisons or contrasts that would be clearer as tables
|
||||
|
||||
**2.4 Formatting Issues**
|
||||
- Missing or inconsistent heading hierarchy
|
||||
- Paragraphs that mix multiple topics
|
||||
- Parallel items written as prose instead of lists
|
||||
- Code, commands, or technical terms not marked as code
|
||||
- Obvious typos or formatting errors
|
||||
|
||||
**Save analysis to file**: `{original-filename}-analysis.md`
|
||||
|
||||
The analysis file serves as the blueprint for Step 3. Use this format:
|
||||
|
||||
```markdown
|
||||
# Content Analysis: {filename}
|
||||
|
||||
## Highlights & Key Insights
|
||||
- [list findings]
|
||||
|
||||
## Structure Assessment
|
||||
- Current flow: [describe]
|
||||
- Suggested sections: [list heading candidates with brief rationale]
|
||||
|
||||
## Reader-Important Information
|
||||
- [list actionable items, key concepts, buried lists, potential tables]
|
||||
|
||||
## Formatting Issues
|
||||
- [list specific issues with location references]
|
||||
|
||||
## Typos Found
|
||||
- [list any obvious typos with corrections, or "None found"]
|
||||
```
|
||||
|
||||
### Step 3: Check/Create Frontmatter, Title & Summary
|
||||
|
||||
Check for YAML frontmatter (`---` block). Create if missing.
|
||||
|
||||
| Field | Processing |
|
||||
|-------|------------|
|
||||
| `title` | See **Title Generation** below |
|
||||
| `slug` | Infer from file path or generate from title |
|
||||
| `summary` | See **Summary Generation** below |
|
||||
| `coverImage` | Check if `imgs/cover.png` exists in same directory; if so, use relative path |
|
||||
|
||||
**Title Generation:**
|
||||
|
||||
Whether or not a title already exists, always run the title optimization flow (unless `auto_select_title` is set).
|
||||
|
||||
**Preparation** — read the full text and extract:
|
||||
- Core argument (one sentence: "what is this article about?")
|
||||
- Most impactful opinion or conclusion
|
||||
- Reader pain point or curiosity trigger
|
||||
- Most memorable metaphor or golden quote
|
||||
|
||||
**Generate 3-4 style-differentiated candidates:**
|
||||
|
||||
| Style | Characteristics | Example |
|
||||
|-------|----------------|---------|
|
||||
| Subversive | Deny common practice, create conflict | "All de-AI-flavor prompts are wrong" |
|
||||
| Solution | Give the answer directly, promise value | "One recipe to make AI write in your voice" |
|
||||
| Suspense | Reveal half, spark curiosity | "It took me six months to find how to remove AI flavor" |
|
||||
| Concrete number | Use numbers for credibility | "150 lines of docs taught AI my writing style" |
|
||||
|
||||
Present to user:
|
||||
|
||||
```
|
||||
Pick a title:
|
||||
|
||||
1. [Title A] — (recommended)
|
||||
2. [Title B] — [style note]
|
||||
3. [Title C] — [style note]
|
||||
|
||||
Enter number, or type a custom title:
|
||||
```
|
||||
|
||||
Put the strongest hook first and mark it (recommended).
|
||||
|
||||
**Title principles:**
|
||||
- **Hook in first 5 chars**: create information gap or cognitive conflict
|
||||
- **Specific > abstract**: "150 lines" beats "a document"
|
||||
- **Negation > affirmation**: "you're doing it wrong" beats "the right way"
|
||||
- **Conversational**: like chatting with a friend, not a paper title
|
||||
- **Max ~25 chars**: longer titles get truncated in feeds
|
||||
- **Accurate, not clickbait**: the article must deliver what the title promises
|
||||
|
||||
**Prohibited patterns:**
|
||||
- "浅谈 XX"、"关于 XX 的思考"、"XX 的探索与实践"
|
||||
- "震惊!"、"万字长文"、"建议收藏"
|
||||
- Pure questions without direction: "AI 写作的未来在哪里?"
|
||||
|
||||
If first line is H1, extract to frontmatter and remove from body. If frontmatter already has `title`, include it as context but still generate fresh candidates.
|
||||
|
||||
**Summary Generation:**
|
||||
|
||||
Generate 3 candidate summaries with different angles. Present to user:
|
||||
|
||||
```
|
||||
Pick a summary:
|
||||
|
||||
1. [Summary A] — [focus note]
|
||||
2. [Summary B] — [focus note]
|
||||
3. [Summary C] — [focus note]
|
||||
|
||||
Enter number, or type a custom summary:
|
||||
```
|
||||
|
||||
**Summary principles:**
|
||||
- 80-150 characters, precise and information-rich
|
||||
- Convey **core value** to the reader, not just the topic
|
||||
- Vary angles: problem-driven, result-driven, insight-driven
|
||||
- **Hook the reader**: make them want to read the full article
|
||||
- Use concrete details (numbers, outcomes, specific methods) over vague descriptions
|
||||
|
||||
**Prohibited patterns:**
|
||||
- "本文介绍了..."、"本文探讨了..."
|
||||
- Pure topic description without value proposition
|
||||
- Repeating the title in different words
|
||||
|
||||
If frontmatter already has `summary`, skip selection and use it.
|
||||
|
||||
**EXTEND.md skip behavior:** If `auto_select: true` is set in EXTEND.md, skip title and summary selection — generate the best candidate directly without asking. User can also set `auto_select_title: true` or `auto_select_summary: true` independently.
|
||||
|
||||
Once title is in frontmatter, body should NOT have H1 (avoid duplication).
|
||||
|
||||
### Step 4: Format Content
|
||||
|
||||
Apply formatting guided by the Step 2 analysis. The goal is making the content scannable and the key points impossible to miss.
|
||||
|
||||
**Formatting toolkit:**
|
||||
|
||||
| Element | When to use | Format |
|
||||
|---------|-------------|--------|
|
||||
| Headings | Natural topic boundaries, section breaks | `##`, `###` hierarchy |
|
||||
| Bold | Key conclusions, important terms, core takeaways | `**bold**` |
|
||||
| Unordered lists | Parallel items, feature lists, examples | `- item` |
|
||||
| Ordered lists | Sequential steps, ranked items, procedures | `1. item` |
|
||||
| Tables | Comparisons, structured data, option matrices | Markdown table |
|
||||
| Code | Commands, file paths, technical terms, variable names | `` `inline` `` or fenced blocks |
|
||||
| Blockquotes | Notable quotes, important warnings, cited text | `> quote` |
|
||||
| Separators | Major topic transitions | `---` |
|
||||
|
||||
**Formatting principles — what NOT to do:**
|
||||
- Do NOT add sentences, explanations, or commentary
|
||||
- Do NOT delete or shorten any content
|
||||
- Do NOT rephrase or rewrite the author's words
|
||||
- Do NOT add headings that editorialize (e.g., "Amazing Discovery" — use neutral descriptive headings)
|
||||
- Do NOT over-format: not every sentence needs bold, not every paragraph needs a heading
|
||||
|
||||
**Formatting principles — what TO do:**
|
||||
- Preserve the author's voice, tone, and every word
|
||||
- **Bold key conclusions and core takeaways** — the sentences a reader would highlight
|
||||
- Extract parallel items from prose into lists only when the structure is clearly there
|
||||
- Add headings where the topic genuinely shifts — prefer vivid, specific headings over generic ones (e.g., "3 天搞定 vs 传统方案" over "方案对比")
|
||||
- Use tables for comparisons or structured data buried in prose
|
||||
- Use blockquotes for golden quotes, memorable statements, or important warnings
|
||||
- Fix obvious typos (based on Step 2 findings)
|
||||
|
||||
### Step 5: Save Formatted File
|
||||
|
||||
Save as `{original-filename}-formatted.md`
|
||||
|
||||
**Backup existing file:**
|
||||
|
||||
```bash
|
||||
if [ -f "{filename}-formatted.md" ]; then
|
||||
mv "{filename}-formatted.md" "{filename}-formatted.backup-$(date +%Y%m%d-%H%M%S).md"
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 6: Execute Typography Script
|
||||
|
||||
Run the formatting script on the output file:
|
||||
|
||||
```bash
|
||||
${BUN_X} {baseDir}/scripts/main.ts {output-file-path} [options]
|
||||
```
|
||||
|
||||
**Script Options:**
|
||||
|
||||
| Option | Short | Description | Default |
|
||||
|--------|-------|-------------|---------|
|
||||
| `--quotes` | `-q` | Replace ASCII quotes with fullwidth quotes `"..."` | false |
|
||||
| `--no-quotes` | | Do not replace quotes | |
|
||||
| `--spacing` | `-s` | Add CJK/English spacing via autocorrect | true |
|
||||
| `--no-spacing` | | Do not add CJK/English spacing | |
|
||||
| `--emphasis` | `-e` | Fix CJK emphasis punctuation issues | true |
|
||||
| `--no-emphasis` | | Do not fix CJK emphasis issues | |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Default: spacing + emphasis enabled, quotes disabled
|
||||
${BUN_X} {baseDir}/scripts/main.ts article.md
|
||||
|
||||
# Enable all features including quote replacement
|
||||
${BUN_X} {baseDir}/scripts/main.ts article.md --quotes
|
||||
|
||||
# Only fix emphasis issues, skip spacing
|
||||
${BUN_X} {baseDir}/scripts/main.ts article.md --no-spacing
|
||||
```
|
||||
|
||||
**Script performs (based on options):**
|
||||
1. Fix CJK emphasis/bold punctuation issues (default: enabled)
|
||||
2. Add CJK/English mixed text spacing via autocorrect (default: enabled)
|
||||
3. Replace ASCII quotes with fullwidth quotes (default: disabled)
|
||||
4. Format frontmatter YAML (always enabled)
|
||||
|
||||
### Step 7: Completion Report
|
||||
|
||||
Display a report summarizing all changes made:
|
||||
|
||||
```
|
||||
**Formatting Complete**
|
||||
|
||||
**Files:**
|
||||
- Analysis: {filename}-analysis.md
|
||||
- Formatted: {filename}-formatted.md
|
||||
|
||||
**Content Analysis Summary:**
|
||||
- Highlights found: X key insights
|
||||
- Golden quotes: X memorable sentences
|
||||
- Formatting issues fixed: X items
|
||||
|
||||
**Changes Applied:**
|
||||
- Frontmatter: [added/updated] (title, slug, summary)
|
||||
- Headings added: X (##: N, ###: N)
|
||||
- Bold markers added: X
|
||||
- Lists created: X (from prose → list conversion)
|
||||
- Tables created: X
|
||||
- Code markers added: X
|
||||
- Blockquotes added: X
|
||||
- Typos fixed: X [list each: "original" → "corrected"]
|
||||
|
||||
**Typography Script:**
|
||||
- CJK spacing: [applied/skipped]
|
||||
- Emphasis fixes: [applied/skipped]
|
||||
- Quote replacement: [applied/skipped]
|
||||
```
|
||||
|
||||
Adjust the report to reflect actual changes — omit categories where no changes were made.
|
||||
|
||||
## Notes
|
||||
|
||||
- Preserve original writing style and tone
|
||||
- Specify correct language for code blocks (e.g., `python`, `javascript`)
|
||||
- Maintain CJK/English spacing standards
|
||||
- The analysis file is a working document — it helps maintain consistency between what was identified and what was formatted
|
||||
|
||||
## Extension Support
|
||||
|
||||
Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options.
|
||||
@@ -0,0 +1,10 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import process from "node:process";
|
||||
|
||||
export function applyAutocorrect(filePath: string): boolean {
|
||||
const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx";
|
||||
const result = spawnSync(npxCmd, ["autocorrect-node", "--fix", filePath], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
return result.status === 0;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-stringify": "^11.0.0",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-visit": "^5.1.0",
|
||||
"yaml": "^2.8.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
||||
|
||||
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
|
||||
|
||||
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
||||
|
||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
|
||||
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
|
||||
|
||||
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||
|
||||
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="],
|
||||
|
||||
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||
|
||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||
|
||||
"fault": ["fault@2.0.1", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ=="],
|
||||
|
||||
"format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="],
|
||||
|
||||
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||
|
||||
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
|
||||
|
||||
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
|
||||
|
||||
"mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
|
||||
|
||||
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
|
||||
|
||||
"mdast-util-frontmatter": ["mdast-util-frontmatter@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "escape-string-regexp": "^5.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0" } }, "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA=="],
|
||||
|
||||
"mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="],
|
||||
|
||||
"mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="],
|
||||
|
||||
"mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="],
|
||||
|
||||
"mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="],
|
||||
|
||||
"mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="],
|
||||
|
||||
"mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="],
|
||||
|
||||
"mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="],
|
||||
|
||||
"mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="],
|
||||
|
||||
"mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
|
||||
|
||||
"micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
|
||||
|
||||
"micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
|
||||
|
||||
"micromark-extension-frontmatter": ["micromark-extension-frontmatter@2.0.0", "", { "dependencies": { "fault": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg=="],
|
||||
|
||||
"micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="],
|
||||
|
||||
"micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="],
|
||||
|
||||
"micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="],
|
||||
|
||||
"micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="],
|
||||
|
||||
"micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="],
|
||||
|
||||
"micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="],
|
||||
|
||||
"micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="],
|
||||
|
||||
"micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="],
|
||||
|
||||
"micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="],
|
||||
|
||||
"micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="],
|
||||
|
||||
"micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="],
|
||||
|
||||
"micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="],
|
||||
|
||||
"micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
|
||||
|
||||
"micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="],
|
||||
|
||||
"micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="],
|
||||
|
||||
"micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="],
|
||||
|
||||
"micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="],
|
||||
|
||||
"micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="],
|
||||
|
||||
"micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
|
||||
|
||||
"micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="],
|
||||
|
||||
"micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="],
|
||||
|
||||
"micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="],
|
||||
|
||||
"micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
|
||||
|
||||
"micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="],
|
||||
|
||||
"micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
|
||||
|
||||
"micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"remark-frontmatter": ["remark-frontmatter@5.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-frontmatter": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0", "unified": "^11.0.0" } }, "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ=="],
|
||||
|
||||
"remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
|
||||
|
||||
"remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
|
||||
|
||||
"remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="],
|
||||
|
||||
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
||||
|
||||
"unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
|
||||
|
||||
"unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
|
||||
|
||||
"unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
|
||||
|
||||
"unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="],
|
||||
|
||||
"unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
|
||||
|
||||
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||
|
||||
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
|
||||
|
||||
"yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
|
||||
|
||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
import { unified } from "unified";
|
||||
import remarkParse from "remark-parse";
|
||||
import remarkCjkFriendly from "remark-cjk-friendly";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkFrontmatter from "remark-frontmatter";
|
||||
import remarkStringify from "remark-stringify";
|
||||
import { visit } from "unist-util-visit";
|
||||
import YAML from "yaml";
|
||||
import { replaceQuotes } from "./quotes";
|
||||
import { applyAutocorrect } from "./autocorrect";
|
||||
|
||||
export interface FormatOptions {
|
||||
quotes?: boolean;
|
||||
spacing?: boolean;
|
||||
emphasis?: boolean;
|
||||
}
|
||||
|
||||
export interface FormatResult {
|
||||
success: boolean;
|
||||
filePath: string;
|
||||
quotesFixed: boolean;
|
||||
spacingApplied: boolean;
|
||||
emphasisFixed: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
const DEFAULT_OPTIONS: Required<FormatOptions> = {
|
||||
quotes: false,
|
||||
spacing: true,
|
||||
emphasis: true,
|
||||
};
|
||||
|
||||
function decodeHtmlEntities(text: string): string {
|
||||
return text.replace(/&#x([0-9A-Fa-f]+);/g, (_, hex) =>
|
||||
String.fromCodePoint(parseInt(hex, 16))
|
||||
);
|
||||
}
|
||||
|
||||
function formatFrontmatter(value: string): string | null {
|
||||
try {
|
||||
const doc = YAML.parseDocument(value);
|
||||
return doc.toString({ lineWidth: 0 }).trimEnd();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function formatMarkdownContent(
|
||||
content: string,
|
||||
options: Required<FormatOptions>
|
||||
): string {
|
||||
const processor = unified()
|
||||
.use(remarkParse)
|
||||
.use(options.emphasis ? remarkCjkFriendly : [])
|
||||
.use(remarkGfm)
|
||||
.use(remarkFrontmatter, ["yaml"])
|
||||
.use(remarkStringify, {
|
||||
wrap: false,
|
||||
});
|
||||
|
||||
const tree = processor.parse(content);
|
||||
|
||||
visit(tree, (node) => {
|
||||
if (node.type === "text" && options.quotes) {
|
||||
const textNode = node as { value: string };
|
||||
textNode.value = replaceQuotes(textNode.value);
|
||||
return;
|
||||
}
|
||||
if (node.type === "yaml") {
|
||||
const yamlNode = node as { value: string };
|
||||
const formatted = formatFrontmatter(yamlNode.value);
|
||||
if (formatted !== null) {
|
||||
yamlNode.value = formatted;
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
let result = processor.stringify(tree);
|
||||
if (options.emphasis) {
|
||||
result = decodeHtmlEntities(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function formatMarkdown(
|
||||
filePath: string,
|
||||
options?: FormatOptions
|
||||
): FormatResult {
|
||||
const opts: Required<FormatOptions> = { ...DEFAULT_OPTIONS, ...options };
|
||||
|
||||
const result: FormatResult = {
|
||||
success: false,
|
||||
filePath,
|
||||
quotesFixed: false,
|
||||
spacingApplied: false,
|
||||
emphasisFixed: false,
|
||||
};
|
||||
|
||||
try {
|
||||
const content = readFileSync(filePath, "utf-8");
|
||||
const formattedContent = formatMarkdownContent(content, opts);
|
||||
|
||||
result.quotesFixed = opts.quotes;
|
||||
result.emphasisFixed = opts.emphasis;
|
||||
|
||||
writeFileSync(filePath, formattedContent, "utf-8");
|
||||
|
||||
if (opts.spacing) {
|
||||
result.spacingApplied = applyAutocorrect(filePath);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
console.log(`✓ Formatted: ${filePath}`);
|
||||
} catch (error) {
|
||||
result.error = error instanceof Error ? error.message : String(error);
|
||||
console.error(`✗ Format failed: ${result.error}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { filePath: string; options: FormatOptions } {
|
||||
const options: FormatOptions = {};
|
||||
let filePath = "";
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
|
||||
if (arg === "--quotes" || arg === "-q") {
|
||||
options.quotes = true;
|
||||
} else if (arg === "--no-quotes") {
|
||||
options.quotes = false;
|
||||
} else if (arg === "--spacing" || arg === "-s") {
|
||||
options.spacing = true;
|
||||
} else if (arg === "--no-spacing") {
|
||||
options.spacing = false;
|
||||
} else if (arg === "--emphasis" || arg === "-e") {
|
||||
options.emphasis = true;
|
||||
} else if (arg === "--no-emphasis") {
|
||||
options.emphasis = false;
|
||||
} else if (arg === "--help" || arg === "-h") {
|
||||
console.log(`Usage: npx -y bun scripts/main.ts <file.md> [options]
|
||||
|
||||
Options:
|
||||
-q, --quotes Replace ASCII quotes with fullwidth quotes (default: false)
|
||||
--no-quotes Do not replace quotes
|
||||
-s, --spacing Add CJK/English spacing via autocorrect (default: true)
|
||||
--no-spacing Do not add CJK/English spacing
|
||||
-e, --emphasis Fix CJK emphasis punctuation issues (default: true)
|
||||
--no-emphasis Do not fix CJK emphasis issues
|
||||
-h, --help Show this help message`);
|
||||
process.exit(0);
|
||||
} else if (!arg.startsWith("-")) {
|
||||
filePath = arg;
|
||||
}
|
||||
}
|
||||
|
||||
return { filePath, options };
|
||||
}
|
||||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
const { filePath, options } = parseArgs(process.argv.slice(2));
|
||||
|
||||
if (!filePath) {
|
||||
console.error("Usage: npx -y bun scripts/main.ts <file.md> [options]");
|
||||
console.error("Use --help for more information.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const result = formatMarkdown(filePath, options);
|
||||
if (!result.success) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user