Compare commits

...

12 Commits

Author SHA1 Message Date
Jim Liu 宝玉 b12ae7374d chore: release v1.22.0 2026-01-25 21:05:37 -06:00
Jim Liu 宝玉 1afa6f5a58 refactor(baoyu-post-to-x): simplify image placeholders from bracket format to XIMGPH 2026-01-25 21:05:08 -06:00
Jim Liu 宝玉 977598d5ae feat(baoyu-post-to-wechat): add theme selection, HTML preview, and simplify image placeholders 2026-01-25 21:05:04 -06:00
Jim Liu 宝玉 eb738aa61a feat(baoyu-cover-image): add default_output_dir preference with output directory selection 2026-01-25 21:04:59 -06:00
Jim Liu 宝玉 e9a030f917 feat(baoyu-article-illustrator): add imgs-subdir output option and improve style selection 2026-01-25 21:04:56 -06:00
Jim Liu 宝玉 e6a9b217e6 chore: release v1.21.4 2026-01-25 16:10:08 -06:00
Jim Liu 宝玉 ec1743592c fix(baoyu-post-to-wechat): fix regressions from Windows compatibility PR
- Fix broken md-to-wechat-fixed.ts/wechat-article-fixed.ts filename
  references that cause runtime crash (files were never renamed)
- Restore frontmatter quote stripping for title/summary values
- Restore --title CLI parameter functionality (was silently ignored)
- Fix summary extraction to properly skip headings, quotes, lists
- Fix argument parsing to reject single-dash args as file paths
- Remove debug console.error logs left from development
2026-01-25 16:09:24 -06:00
Jim Liu 宝玉 fe647a11bb Merge pull request #20 from JadeLiang003/fix/windows-copy-paste-support
Fix: Windows compatibility for baoyu-post-to-wechat
2026-01-25 16:04:01 -06:00
JadeLiang 5a11b49b00 Fix: Windows compatibility for baoyu-post-to-wechat
## Problem
The baoyu-post-to-wechat skill failed on Windows due to:
1. `new URL(import.meta.url).pathname` returns incorrect path format on Windows
2. Copy/paste operations used `xdotool` (Linux tool) which doesn't exist on Windows

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

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

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 15:24:04 +08:00
Jim Liu 宝玉 04692515bb fix: remove opacity of watermark 2026-01-24 17:23:04 -06:00
Jim Liu 宝玉 eb54c2a2e7 chore: release v1.21.3 2026-01-24 17:20:02 -06:00
Jim Liu 宝玉 a39c8eb0bd refactor(baoyu-article-illustrator): extract content to reference files
- Simplify SKILL.md by extracting usage and prompt templates
- Add references/usage.md for command syntax
- Add references/prompt-construction.md for prompt templates
- Add default_output_dir preference option
- Reorganize workflow from 5 to 6 steps with Pre-check phase
2026-01-24 17:19:42 -06:00
21 changed files with 576 additions and 424 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
}, },
"metadata": { "metadata": {
"description": "Skills shared by Baoyu for improving daily work efficiency", "description": "Skills shared by Baoyu for improving daily work efficiency",
"version": "1.21.2" "version": "1.22.0"
}, },
"plugins": [ "plugins": [
{ {
+22
View File
@@ -2,6 +2,28 @@
English | [中文](./CHANGELOG.zh.md) English | [中文](./CHANGELOG.zh.md)
## 1.22.0 - 2026-01-25
### Features
- `baoyu-article-illustrator`: adds `imgs-subdir` output directory option; improves style selection to always ask and show preferred_style from EXTEND.md.
- `baoyu-cover-image`: adds `default_output_dir` preference supporting `same-dir`, `imgs-subdir`, and `independent` options with Step 1.5 for output directory selection.
- `baoyu-post-to-wechat`: adds theme selection (default/grace/simple) with AskUserQuestion before posting; adds HTML preview step; simplifies image placeholders to `WECHATIMGPH_N` format; refactors copy/paste to cross-platform helpers.
### Refactor
- `baoyu-post-to-x`: simplifies image placeholders from `[[IMAGE_PLACEHOLDER_N]]` to `XIMGPH_N` format.
## 1.21.4 - 2026-01-25
### Fixes
- `baoyu-post-to-wechat`: adds Windows compatibility—uses `fileURLToPath` for correct path resolution, replaces system-dependent copy/paste tools (osascript/xdotool) with CDP keyboard events for cross-platform support.
- `baoyu-post-to-wechat`: fixes regressions from Windows compatibility PR—corrects broken `-fixed` filename references, restores frontmatter quote stripping, restores `--title` CLI parameter, fixes summary extraction to skip headings/quotes/lists, fixes argument parsing for single-dash flags, removes debug logs.
- `baoyu-article-illustrator`, `baoyu-cover-image`, `baoyu-xhs-images`: removes opacity option from watermark configuration.
## 1.21.3 - 2026-01-24
### Refactor
- `baoyu-article-illustrator`: simplifies SKILL.md by extracting content to reference files—adds `references/usage.md` for command syntax, `references/prompt-construction.md` for prompt templates. Reorganizes workflow from 5 to 6 steps with new Pre-check phase. Adds `default_output_dir` preference option.
## 1.21.2 - 2026-01-24 ## 1.21.2 - 2026-01-24
### Features ### Features
+22
View File
@@ -2,6 +2,28 @@
[English](./CHANGELOG.md) | 中文 [English](./CHANGELOG.md) | 中文
## 1.22.0 - 2026-01-25
### 新功能
- `baoyu-article-illustrator`:新增 `imgs-subdir` 输出目录选项;改进风格选择,始终询问并展示 EXTEND.md 中的 preferred_style。
- `baoyu-cover-image`:新增 `default_output_dir` 偏好设置,支持 `same-dir``imgs-subdir``independent` 选项,新增 Step 1.5 输出目录选择流程。
- `baoyu-post-to-wechat`:发布前新增主题选择(default/grace/simple);新增 HTML 预览步骤;图片占位符简化为 `WECHATIMGPH_N` 格式;重构复制粘贴为跨平台辅助函数。
### 重构
- `baoyu-post-to-x`:图片占位符从 `[[IMAGE_PLACEHOLDER_N]]` 简化为 `XIMGPH_N` 格式。
## 1.21.4 - 2026-01-25
### 修复
- `baoyu-post-to-wechat`:新增 Windows 兼容性——使用 `fileURLToPath` 正确解析路径,将系统依赖的复制粘贴工具(osascript/xdotool)替换为 CDP 键盘事件,实现跨平台支持。
- `baoyu-post-to-wechat`:修复 Windows 兼容性 PR 引入的回退问题——修正错误的 `-fixed` 文件名引用、恢复 frontmatter 引号剥离、恢复 `--title` CLI 参数、修复摘要提取逻辑以正确跳过标题/引用/列表、修复单横线参数解析、移除调试日志。
- `baoyu-article-illustrator``baoyu-cover-image``baoyu-xhs-images`:移除水印配置中的透明度选项。
## 1.21.3 - 2026-01-24
### 重构
- `baoyu-article-illustrator`:简化 SKILL.md,提取内容至引用文件——新增 `references/usage.md` 用于命令语法,`references/prompt-construction.md` 用于提示词模板。工作流从 5 步重组为 6 步,新增 Pre-check 预检阶段。新增 `default_output_dir` 偏好设置选项。
## 1.21.2 - 2026-01-24 ## 1.21.2 - 2026-01-24
### 新功能 ### 新功能
+172 -301
View File
@@ -7,236 +7,201 @@ description: Analyzes article structure, identifies positions requiring visual a
Analyze articles, identify illustration positions, generate images with Type × Style consistency. 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 ## Two Dimensions
| Dimension | Controls | Examples | | Dimension | Controls | Examples |
|-----------|----------|----------| |-----------|----------|----------|
| **Type** | Information structure, content layout | infographic, scene, flowchart, comparison, framework, timeline | | **Type** | Information structure, layout | infographic, scene, flowchart, comparison, framework, timeline |
| **Style** | Visual aesthetics, colors, mood | notion, warm, minimal, blueprint, watercolor, elegant | | **Style** | Visual aesthetics, mood | notion, warm, minimal, blueprint, watercolor, elegant |
Type × Style can be freely combined. Example: `--type infographic --style blueprint` creates technical data visualization with schematic aesthetics. Type × Style can be freely combined. Example: `--type infographic --style blueprint`
## Type Gallery ## Type Gallery
| Type | Description | Best For | | Type | Best For |
|------|-------------|----------| |------|----------|
| `infographic` | Data visualization, charts, metrics | Technical articles, data analysis, comparisons | | `infographic` | Data, metrics, technical articles |
| `scene` | Atmospheric illustration, mood rendering | Narrative articles, personal stories, emotional content | | `scene` | Narratives, personal stories, emotional content |
| `flowchart` | Process diagrams, step visualization | Tutorials, workflows, decision trees | | `flowchart` | Tutorials, workflows, processes |
| `comparison` | Side-by-side, before/after contrast | Product comparisons, option evaluations | | `comparison` | Side-by-side, before/after, options |
| `framework` | Concept maps, relationship diagrams | Methodologies, models, architecture design | | `framework` | Methodologies, models, architecture |
| `timeline` | Chronological progression | History, project progress, evolution | | `timeline` | History, progress, evolution |
## Density Options
| Density | Count | Description |
|---------|-------|-------------|
| `minimal` | 1-2 | Core concepts only |
| `balanced` (Default) | 3-5 | Major sections coverage |
| `rich` | 6+ | Rich visual support |
## Style Gallery ## Style Gallery
| Style | Description | Best For | | Style | Best For |
|-------|-------------|----------| |-------|----------|
| `notion` (Default) | Minimalist hand-drawn line art | Knowledge sharing, SaaS, productivity | | `notion` (Default) | Knowledge sharing, SaaS, productivity |
| `elegant` | Refined, sophisticated | Business, thought leadership | | `elegant` | Business, thought leadership |
| `warm` | Friendly, approachable | Personal growth, lifestyle, education | | `warm` | Personal growth, lifestyle, education |
| `minimal` | Ultra-clean, zen-like | Philosophy, minimalism, core concepts | | `minimal` | Philosophy, core concepts |
| `blueprint` | Technical schematics | Architecture, system design, engineering | | `blueprint` | Architecture, system design |
| `watercolor` | Soft artistic with natural warmth | Lifestyle, travel, creative | | `watercolor` | Lifestyle, travel, creative |
| `editorial` | Magazine-style infographic | Tech explainers, journalism | | `editorial` | Tech explainers, journalism |
| `scientific` | Academic precise diagrams | Biology, chemistry, technical research | | `scientific` | Academic, technical research |
Full style specifications: `references/styles/<style>.md` Full styles: [references/styles.md](references/styles.md)
## Type × Style Compatibility
| | notion | warm | minimal | blueprint | watercolor | elegant | editorial | scientific |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| infographic | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
| scene | ✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ | ✗ |
| flowchart | ✓✓ | ✓ | ✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ |
| comparison | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ |
| framework | ✓✓ | ✓ | ✓✓ | ✓✓ | ✗ | ✓✓ | ✓ | ✓✓ |
| timeline | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## Auto Selection ## Auto Selection
| Content Signals | Recommended Type | Recommended Style | | Content Signals | Type | Style |
|-----------------|------------------|-------------------| |-----------------|------|-------|
| API, metrics, data, comparison, numbers | infographic | blueprint, notion | | API, metrics, data, numbers | infographic | blueprint, notion |
| Story, emotion, journey, experience, personal | scene | warm, watercolor | | Story, emotion, journey | scene | warm, watercolor |
| How-to, steps, workflow, process, tutorial | flowchart | notion, minimal | | How-to, steps, workflow | flowchart | notion, minimal |
| vs, pros/cons, before/after, alternatives | comparison | notion, elegant | | vs, pros/cons, before/after | comparison | notion, elegant |
| Framework, model, architecture, principles | framework | blueprint, notion | | Framework, model, architecture | framework | blueprint, notion |
| History, timeline, progress, evolution | timeline | elegant, warm | | History, timeline, progress | timeline | elegant, warm |
## Output Directory
```
illustrations/{topic-slug}/
├── source-{slug}.{ext}
├── outline.md
├── prompts/
│ └── illustration-{slug}.md
└── NN-{type}-{slug}.png
```
**Slug**: Extract 2-4 word topic in kebab-case.
**Conflict**: Append `-YYYYMMDD-HHMMSS` if exists.
## Workflow ## Workflow
### Progress Copy this checklist and track progress:
``` ```
- [ ] Step 1: Setup & Analyze Progress:
- [ ] Step 2: Confirm Settings ⚠️ REQUIRED - [ ] Step 1: Pre-check
- [ ] Step 3: Generate Outline - [ ] Step 2: Setup & Analyze
- [ ] Step 4: Generate Images - [ ] Step 3: Confirm Settings ⚠️ REQUIRED
- [ ] Step 5: Finalize - [ ] Step 4: Generate Outline
- [ ] Step 5: Generate Images
- [ ] Step 6: Finalize
``` ```
### Step 1: Setup & Analyze ---
**1.1 Load Preferences (EXTEND.md)** ### Step 1: Pre-check
Use Bash to check EXTEND.md existence (priority order): **1.1 Determine Input Type**
| Input | Output Directory | Next |
|-------|------------------|------|
| File path | Ask user (1.2) | → 1.2 |
| Pasted content | `illustrations/{topic-slug}/` | → 1.4 |
**1.2 Determine Output Directory** (file path input only)
Check `default_output_dir` in preferences:
| Preference Value | Action |
|------------------|--------|
| `same-dir` | Use `{article-dir}/`, display "Output: {path}" |
| `imgs-subdir` | Use `{article-dir}/imgs/`, display "Output: {path}" |
| `illustrations-subdir` | Use `{article-dir}/illustrations/`, display "Output: {path}" |
| `independent` | Use `illustrations/{topic-slug}/`, display "Output: {path}" |
| Not configured | **MUST** ask with AskUserQuestion ↓ |
**AskUserQuestion** (when no preference):
- `{article-dir}/` - Same directory as article
- `{article-dir}/imgs/` - Images subdirectory
- `{article-dir}/illustrations/` - Illustrations subdirectory (Recommended)
- `illustrations/{topic-slug}/` - Independent directory
- Save as default - Remember this choice for future runs
**1.3 Check Existing Images**
Scan target directory for `.png/.jpg/.webp` files.
If images exist → AskUserQuestion: How to handle?
- `supplement` - Keep existing, generate only new positions
- `overwrite` - Overwrite same-name files
- `regenerate` - Clear all and regenerate
**1.4 Confirm Article Update Method** (file path input only)
AskUserQuestion: How to update article?
- `update` - Modify original file directly
- `copy` - Create `{name}-illustrated.md` copy
**1.5 Load Preferences (EXTEND.md)**
```bash ```bash
# Check project-level first
test -f .baoyu-skills/baoyu-article-illustrator/EXTEND.md && echo "project" 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" test -f "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "user"
``` ```
┌──────────────────────────────────────────────────────────┬───────────────────┐ | Result | Action |
│ Path │ Location │ |--------|--------|
├──────────────────────────────────────────────────────────┼───────────────────┤ | Found | Read, parse, display summary |
│ .baoyu-skills/baoyu-article-illustrator/EXTEND.md │ Project directory │ | Not found | Ask with AskUserQuestion (see references/config/first-time-setup.md) |
├──────────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md │ User home │
└──────────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐ **Supports**: Watermark | Preferred type/style | Custom styles | Language | Output directory
│ 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` ### Step 2: Setup & Analyze
**1.2 Analyze Content** **2.1 Analyze Content**
Read article, detect language, classify content.
| Analysis | Description | | Analysis | Description |
|----------|-------------| |----------|-------------|
| Content type | Technical / Tutorial / Methodology / Narrative | | Content type | Technical / Tutorial / Methodology / Narrative |
| Core arguments | 2-5 main points that MUST be visualized | | Core arguments | 2-5 main points to visualize |
| Visual opportunities | Positions where illustrations add value | | Visual opportunities | Positions where illustrations add value |
| Recommended type | Based on content signals | | Recommended type | Based on content signals |
| Recommended density | Based on article length and complexity | | Recommended density | Based on length and complexity |
**1.3 Extract Core Arguments** **2.2 Extract Core Arguments**
Extract 2-5 core arguments that MUST be visualized:
- Main thesis - Main thesis
- Key concepts reader needs - Key concepts reader needs
- Comparisons/contrasts being made - Comparisons/contrasts
- Framework/model proposed - Framework/model proposed
**CRITICAL**: If article uses metaphors (e.g., "电锯切西瓜"), do NOT illustrate literally. Visualize the **underlying concept** instead. **CRITICAL**: If article uses metaphors (e.g., "电锯切西瓜"), do NOT illustrate literally. Visualize the **underlying concept**.
**1.4 Identify Positions** **2.3 Identify Positions**
**What to Illustrate**: **Illustrate**:
- Core arguments (REQUIRED) - Core arguments (REQUIRED)
- Abstract concepts needing visualization - Abstract concepts
- Data comparisons, metrics - Data comparisons
- Processes, workflows - Processes, workflows
**What NOT to Illustrate**: **Do NOT Illustrate**:
- Metaphors literally - Metaphors literally
- Decorative scenes without information - Decorative scenes
- Generic illustrations - Generic illustrations
### Step 2: Confirm Settings ⚠️ ---
### Step 3: Confirm Settings ⚠️
**Do NOT skip.** Use AskUserQuestion with 3-4 questions in ONE call. **Do NOT skip.** Use AskUserQuestion with 3-4 questions in ONE call.
**Question 1: Illustration Type** **Q1: Illustration Type**
- [Recommended based on analysis] (Recommended)
- infographic / scene / flowchart / comparison / framework / timeline / mixed
Based on content analysis, recommend type: **Q2: Density**
- [Recommended type based on signals] (Recommended) - minimal (1-2) - Core concepts only
- infographic - Data visualization, charts - balanced (3-5) (Recommended) - Major sections
- scene - Atmospheric, mood rendering - rich (6+) - Comprehensive support
- flowchart - Process, steps
- comparison - Side-by-side contrast
- framework - Concept relationships
- timeline - Chronological progression
- mixed - Combine multiple types
**Question 2: Density** **Q3: Style** (ALWAYS ask, even with preferred_style in EXTEND.md)
- minimal (1-2 images) - Core concepts only If EXTEND.md has `preferred_style`:
- balanced (3-5 images) (Recommended) - Major sections - [Custom style name + brief description] (Recommended)
- rich (6+ images) - Comprehensive visual support - [Top compatible built-in style 1]
- [Top compatible built-in style 2]
- [Top compatible built-in style 3]
**Question 3: Style** If no `preferred_style`:
- [Best compatible from matrix] (Recommended)
- [Other ✓✓ style 1]
- [Other ✓✓ style 2]
- [Other ✓ style]
Based on recommended Type, suggest compatible styles (see Type × Style Compatibility matrix): Style selection based on Type × Style compatibility matrix (references/styles.md).
- [Best compatible style for recommended type] (Recommended) Full specs: `references/styles/<style>.md`
- [Other highly compatible styles: ✓✓ from matrix]
- [Compatible styles: ✓ from matrix]
**Question 4** (only if source ≠ user language): **Q4** (only if source ≠ user language):
- Language: Source language / User language - Language: Source / User language
### Step 3: Generate Outline ---
Based on confirmed Type + Density + Style, generate illustration outline. ### Step 4: Generate Outline
**Outline Format** (`outline.md`): Save as `outline.md`:
```yaml ```yaml
--- ---
@@ -249,44 +214,48 @@ image_count: 4
## Illustration 1 ## Illustration 1
**Position**: [section] / [paragraph] **Position**: [section] / [paragraph]
**Purpose**: [why this illustration helps] **Purpose**: [why this helps]
**Visual Content**: [what to show] **Visual Content**: [what to show]
**Type Application**: [how type applies here] **Type Application**: [how type applies]
**Filename**: 01-infographic-concept-name.png **Filename**: 01-infographic-concept-name.png
## Illustration 2 ## Illustration 2
... ...
``` ```
**Outline Requirements**: **Requirements**:
- Each illustration position justified by content needs - Each position justified by content needs
- Type applied consistently across all illustrations - Type applied consistently
- Style characteristics reflected in visual descriptions - Style reflected in descriptions
- Count matches density selection - Count matches density
### Step 4: Generate Images ---
**4.1 Create Prompts** ### Step 5: Generate Images
Follow Prompt Construction principles below. Save each to `prompts/illustration-{slug}.md`. **5.1 Create Prompts**
**4.2 Select Generation Skill** Follow [references/prompt-construction.md](references/prompt-construction.md). Save to `prompts/illustration-{slug}.md`.
Check available image generation skills. If multiple, ask user to choose. **5.2 Select Generation Skill**
**4.3 Apply Watermark** (if enabled in preferences) Check available skills. If multiple, ask user.
Add to prompt: `Include a subtle watermark "[content]" positioned at [position] with approximately [opacity*100]% visibility.` **5.3 Apply Watermark** (if enabled)
**4.4 Generate** Add: `Include a subtle watermark "[content]" at [position].`
**5.4 Generate**
1. Generate sequentially 1. Generate sequentially
2. After each: "Generated X/N" 2. After each: "Generated X/N"
3. On failure: auto-retry once, then log and continue 3. On failure: retry once, then log and continue
### Step 5: Finalize ---
**5.1 Update Article** ### Step 6: Finalize
**6.1 Update Article**
Insert after corresponding paragraph: Insert after corresponding paragraph:
```markdown ```markdown
@@ -295,158 +264,60 @@ Insert after corresponding paragraph:
Alt text: concise description in article's language. Alt text: concise description in article's language.
**5.2 Output Summary** **6.2 Output Summary**
``` ```
Article Illustration Complete! Article Illustration Complete!
Article: [path] Article: [path]
Type: [type name] Type: [type] | Density: [level] | Style: [style]
Density: [minimal/balanced/rich] Location: [directory]
Style: [style name]
Location: [directory path]
Images: X/N generated Images: X/N generated
Positions: Positions:
- 01-infographic-xxx.png → After "[Section]" - 01-xxx.png → After "[Section]"
- 02-infographic-yyy.png → After "[Section]" - 02-yyy.png → After "[Section]"
[If failures] [If failures]
Failed: Failed:
- NN-type-zzz.png: [reason] - NN-zzz.png: [reason]
``` ```
## Prompt Construction ---
### Principles ## Output Directory
Good prompts must include:
1. **Layout Structure First**: Describe composition, zones, flow direction
2. **Specific Data/Labels**: Use actual numbers, terms from article
3. **Visual Relationships**: How elements connect
4. **Semantic Colors**: Meaning-based color choices (red=warning, green=efficient)
5. **Style Characteristics**: Line treatment, texture, mood
6. **Aspect Ratio**: End with ratio and complexity level
### Type-Specific Prompts
**Infographic**:
``` ```
[Title] - Data Visualization illustrations/{topic-slug}/
├── source-{slug}.{ext}
Layout: [grid/radial/hierarchical] ├── outline.md
├── prompts/
ZONES: │ └── illustration-{slug}.md
- Zone 1: [data point with specific values] └── NN-{type}-{slug}.png
- 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**: **Slug**: 2-4 word topic in kebab-case.
``` **Conflict**: Append `-YYYYMMDD-HHMMSS` if exists.
[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
## Modification ## Modification
| Action | Steps | | Action | Steps |
|--------|-------| |--------|-------|
| **Edit** | Update prompt → Regenerate → Update reference | | **Edit** | Update prompt → Regenerate → Update reference |
| **Add** | Identify position → Create prompt → Generate → Update outline → Insert reference | | **Add** | Identify position → Create prompt → Generate → Update outline → Insert |
| **Delete** | Delete files → Remove reference → Update outline | | **Delete** | Delete files → Remove reference → Update outline |
## References ## References
| File | Content | | File | Content |
|------|---------| |------|---------|
| [references/styles.md](references/styles.md) | Style gallery & compatibility matrix | | [references/usage.md](references/usage.md) | Command syntax and options |
| [references/styles.md](references/styles.md) | Style gallery & compatibility |
| [references/prompt-construction.md](references/prompt-construction.md) | Prompt templates |
| `references/styles/<style>.md` | Full style specifications | | `references/styles/<style>.md` | Full style specifications |
| `references/config/preferences-schema.md` | EXTEND.md schema | | `references/config/preferences-schema.md` | EXTEND.md schema |
| `references/config/first-time-setup.md` | First-time setup flow | | `references/config/first-time-setup.md` | First-time setup flow |
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. See **Step 1.1** for paths and supported options. Custom configurations via EXTEND.md. See **Step 1.5** for paths and supported options.
@@ -15,7 +15,6 @@ watermark:
enabled: false enabled: false
content: "" content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
opacity: 0.7 # 0.1-1.0
preferred_style: preferred_style:
name: null # Built-in or custom style name name: null # Built-in or custom style name
@@ -23,6 +22,8 @@ preferred_style:
language: null # zh|en|ja|ko|auto language: null # zh|en|ja|ko|auto
default_output_dir: null # same-dir|illustrations-subdir|independent
custom_styles: custom_styles:
- name: my-style - name: my-style
description: "Style description" description: "Style description"
@@ -44,10 +45,10 @@ custom_styles:
| `watermark.enabled` | bool | false | Enable watermark | | `watermark.enabled` | bool | false | Enable watermark |
| `watermark.content` | string | "" | Watermark text (@username or custom) | | `watermark.content` | string | "" | Watermark text (@username or custom) |
| `watermark.position` | enum | bottom-right | Position on image | | `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.name` | string | null | Style name or null |
| `preferred_style.description` | string | "" | Custom notes/override | | `preferred_style.description` | string | "" | Custom notes/override |
| `language` | string | null | Output language (null = auto-detect) | | `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 | | `custom_styles` | array | [] | User-defined styles |
## Position Options ## Position Options
@@ -59,6 +60,14 @@ custom_styles:
| `bottom-center` | Bottom center | | `bottom-center` | Bottom center |
| `top-right` | Upper right corner | | `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 ## Custom Style Fields
| Field | Required | Description | | Field | Required | Description |
@@ -94,7 +103,6 @@ watermark:
enabled: true enabled: true
content: "@myaccount" content: "@myaccount"
position: bottom-right position: bottom-right
opacity: 0.5
preferred_style: preferred_style:
name: notion name: notion
@@ -0,0 +1,127 @@
# Prompt Construction
## Principles
Good prompts must include:
1. **Layout Structure First**: Describe composition, zones, flow direction
2. **Specific Data/Labels**: Use actual numbers, terms from article
3. **Visual Relationships**: How elements connect
4. **Semantic Colors**: Meaning-based color choices (red=warning, green=efficient)
5. **Style Characteristics**: Line treatment, texture, mood
6. **Aspect Ratio**: End with ratio and complexity level
## Type-Specific Templates
### Infographic
```
[Title] - Data Visualization
Layout: [grid/radial/hierarchical]
ZONES:
- Zone 1: [data point with specific values]
- Zone 2: [comparison with metrics]
- Zone 3: [summary/conclusion]
LABELS: [specific numbers, percentages, terms from article]
COLORS: [semantic color mapping]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Scene
```
[Title] - Atmospheric Scene
FOCAL POINT: [main subject]
ATMOSPHERE: [lighting, mood, environment]
MOOD: [emotion to convey]
COLOR TEMPERATURE: [warm/cool/neutral]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Flowchart
```
[Title] - Process Flow
Layout: [left-right/top-down/circular]
STEPS:
1. [Step name] - [brief description]
2. [Step name] - [brief description]
...
CONNECTIONS: [arrow types, decision points]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Comparison
```
[Title] - Comparison View
LEFT SIDE - [Option A]:
- [Point 1]
- [Point 2]
RIGHT SIDE - [Option B]:
- [Point 1]
- [Point 2]
DIVIDER: [visual separator]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Framework
```
[Title] - Conceptual Framework
STRUCTURE: [hierarchical/network/matrix]
NODES:
- [Concept 1] - [role]
- [Concept 2] - [role]
RELATIONSHIPS: [how nodes connect]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Timeline
```
[Title] - Chronological View
DIRECTION: [horizontal/vertical]
EVENTS:
- [Date/Period 1]: [milestone]
- [Date/Period 2]: [milestone]
MARKERS: [visual indicators]
STYLE: [style characteristics]
ASPECT: 16:9
```
## What to Avoid
- Vague descriptions ("a nice image")
- Literal metaphor illustrations
- Missing concrete labels/annotations
- Generic decorative elements
## Watermark Integration
If watermark enabled in preferences, append:
```
Include a subtle watermark "[content]" positioned at [position] with approximately [opacity*100]% visibility.
```
@@ -0,0 +1,66 @@
# Usage
## Command Syntax
```bash
# Auto-select type and style based on content
/baoyu-article-illustrator path/to/article.md
# Specify type
/baoyu-article-illustrator path/to/article.md --type infographic
# Specify style
/baoyu-article-illustrator path/to/article.md --style blueprint
# Combine type and style
/baoyu-article-illustrator path/to/article.md --type flowchart --style notion
# Specify density
/baoyu-article-illustrator path/to/article.md --density rich
# Direct content input (paste mode)
/baoyu-article-illustrator
[paste content]
```
## Options
| Option | Description |
|--------|-------------|
| `--type <name>` | Illustration type (see Type Gallery in SKILL.md) |
| `--style <name>` | Visual style (see references/styles.md) |
| `--density <level>` | Image count: minimal / balanced / rich |
## Input Modes
| Mode | Trigger | Output Directory |
|------|---------|------------------|
| File path | `path/to/article.md` | Use `default_output_dir` preference, or ask if not set |
| Paste content | No path argument | `illustrations/{topic-slug}/` |
## Output Directory Options
| Value | Path |
|-------|------|
| `same-dir` | `{article-dir}/` |
| `illustrations-subdir` | `{article-dir}/illustrations/` |
| `independent` | `illustrations/{topic-slug}/` |
Configure in EXTEND.md: `default_output_dir: illustrations-subdir`
## Examples
**Technical article with data**:
```bash
/baoyu-article-illustrator api-design.md --type infographic --style blueprint
```
**Personal story**:
```bash
/baoyu-article-illustrator journey.md --type scene --style warm
```
**Tutorial with steps**:
```bash
/baoyu-article-illustrator how-to-deploy.md --type flowchart --density rich
```
@@ -15,7 +15,6 @@ watermark:
enabled: false enabled: false
content: "" content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right 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_art: null # ligne-claire|manga|realistic|ink-brush|chalk
preferred_tone: null # neutral|warm|dramatic|romantic|energetic|vintage|action preferred_tone: null # neutral|warm|dramatic|romantic|energetic|vintage|action
@@ -42,7 +41,6 @@ character_presets:
| `watermark.enabled` | bool | false | Enable watermark | | `watermark.enabled` | bool | false | Enable watermark |
| `watermark.content` | string | "" | Watermark text (@username or custom) | | `watermark.content` | string | "" | Watermark text (@username or custom) |
| `watermark.position` | enum | bottom-right | Position on image | | `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_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_tone` | string | null | Tone (neutral, warm, dramatic, romantic, energetic, vintage, action) |
| `preferred_layout` | string | null | Layout preference or null | | `preferred_layout` | string | null | Layout preference or null |
@@ -113,7 +111,6 @@ watermark:
enabled: true enabled: true
content: "@comicstudio" content: "@comicstudio"
position: bottom-right position: bottom-right
opacity: 0.4
preferred_art: manga preferred_art: manga
preferred_tone: neutral 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 | | `bottom-center` | Webtoon vertical scroll, centered designs | Text-heavy bottom area |
| `top-right` | **Not recommended for comics** | Always - conflicts with page numbers | | `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 ## Content Format
| Format | Example | Style | | 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: When watermark is enabled, add to image generation prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position] Include a subtle watermark "[content]" positioned at [position].
with approximately [opacity*100]% visibility. The watermark should The watermark should be legible but not distracting from the comic panels
be legible but not distracting from the comic panels and storytelling. and storytelling. Ensure watermark does not overlap speech bubbles or key action.
Ensure watermark does not overlap speech bubbles or key action.
``` ```
## Common Issues ## 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 invisible on dark panels | Adjust contrast or add subtle outline |
| Watermark overlaps speech bubble | Change position or lower on page | | Watermark overlaps speech bubble | Change position or lower on page |
| Watermark inconsistent across pages | Use session ID for consistency | | 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 | | Conflicts with page number | Never use top-right position |
+56 -12
View File
@@ -218,10 +218,17 @@ Default: `balanced`
## File Structure ## File Structure
Each session creates an independent directory named by content slug: Output directory depends on `default_output_dir` preference:
| Preference | Output Path |
|------------|-------------|
| `same-dir` | `{article-dir}/` |
| `imgs-subdir` | `{article-dir}/imgs/` |
| `independent` (default) | `cover-image/{topic-slug}/` |
| Pasted content | `cover-image/{topic-slug}/` (always) |
``` ```
cover-image/{topic-slug}/ <output-dir>/
├── source-{slug}.{ext} # Source files (text, images, etc.) ├── source-{slug}.{ext} # Source files (text, images, etc.)
├── prompts/cover.md # Generation prompt ├── prompts/cover.md # Generation prompt
└── cover.png # Output image └── cover.png # Output image
@@ -250,7 +257,7 @@ Copy and track progress:
``` ```
Cover Image Progress: Cover Image Progress:
- [ ] Step 0: Check preferences (EXTEND.md) ⚠️ REQUIRED if not found - [ ] Step 0: Check preferences (EXTEND.md) ⚠️ REQUIRED if not found
- [ ] Step 1: Analyze content - [ ] Step 1: Analyze content + determine output directory ⚠️ MUST ask if not configured
- [ ] Step 2: Confirm options (4 dimensions) ⚠️ REQUIRED unless --quick or all specified - [ ] Step 2: Confirm options (4 dimensions) ⚠️ REQUIRED unless --quick or all specified
- [ ] Step 3: Create prompt - [ ] Step 3: Create prompt
- [ ] Step 4: Generate image - [ ] Step 4: Generate image
@@ -260,9 +267,9 @@ Cover Image Progress:
### Flow ### Flow
``` ```
Input → [Step 0: Preferences/Setup] → Analyze → [Confirm: 4 Dimensions] → Prompt → Generate → Complete Input → [Step 0: Preferences/Setup] → Analyze → [Output Dir ⚠️] → [Confirm: 4 Dimensions] → Prompt → Generate → Complete
(skip if --quick or all dimensions specified) (skip if --quick or all specified)
``` ```
### Step 0: Load Preferences (EXTEND.md) ⚠️ ### Step 0: Load Preferences (EXTEND.md) ⚠️
@@ -307,6 +314,7 @@ Preferences loaded from [project/user]:
• Text: [preferred_text or "title-only"] • Text: [preferred_text or "title-only"]
• Mood: [preferred_mood or "balanced"] • Mood: [preferred_mood or "balanced"]
• Aspect: [default_aspect] • Aspect: [default_aspect]
• Output: [default_output_dir or "not set — will ask in Step 1.5"]
• Quick mode: [enabled/disabled] • Quick mode: [enabled/disabled]
• Language: [language or "auto"] • Language: [language or "auto"]
``` ```
@@ -369,7 +377,20 @@ options:
Note: More ratios (4:3, 3:2) available during generation. This sets the default recommendation. Note: More ratios (4:3, 3:2) available during generation. This sets the default recommendation.
**Q5: Quick Mode** **Q5: Default Output Directory**
```yaml
header: "Output"
question: "Default output directory for cover images?"
options:
- label: "Independent (Recommended)"
description: "cover-image/{topic-slug}/ - separate from article"
- label: "Same directory"
description: "{article-dir}/ - alongside the article file"
- label: "imgs subdirectory"
description: "{article-dir}/imgs/ - images folder near article"
```
**Q6: Quick Mode**
```yaml ```yaml
header: "Quick" header: "Quick"
question: "Enable quick mode by default?" question: "Enable quick mode by default?"
@@ -380,7 +401,7 @@ options:
description: "Skip confirmation, use auto-selection" description: "Skip confirmation, use auto-selection"
``` ```
**Q6: Save Location** **Q7: Save Location**
```yaml ```yaml
header: "Save" header: "Save"
question: "Where to save preferences?" question: "Where to save preferences?"
@@ -395,7 +416,7 @@ options:
Full setup details: `references/config/first-time-setup.md` Full setup details: `references/config/first-time-setup.md`
**EXTEND.md Supports**: Watermark | Preferred type | Preferred style | Preferred text | Preferred mood | Default aspect ratio | Quick mode | Custom style definitions | Language preference **EXTEND.md Supports**: Watermark | Preferred type | Preferred style | Preferred text | Preferred mood | Default aspect ratio | Default output directory | Quick mode | Custom style definitions | Language preference
Schema: `references/config/preferences-schema.md` Schema: `references/config/preferences-schema.md`
@@ -417,6 +438,30 @@ Read source content, save it if needed, and perform analysis.
- Note user's input language (from conversation) - Note user's input language (from conversation)
- Compare with language preference in EXTEND.md - Compare with language preference in EXTEND.md
**1.5 Determine Output Directory** ⚠️
**MUST ask user when `default_output_dir` is not set in EXTEND.md and input is a file path.**
| Input | Behavior |
|-------|----------|
| File path + preference set | Use configured `default_output_dir` |
| File path + **no preference** | ⚠️ **MUST** ask with AskUserQuestion ↓ |
| Pasted content | `cover-image/{topic-slug}/` (always independent) |
`default_output_dir` preference values:
| Preference Value | Output Path |
|------------------|-------------|
| `same-dir` | `{article-dir}/` |
| `imgs-subdir` | `{article-dir}/imgs/` |
| `independent` | `cover-image/{topic-slug}/` |
**AskUserQuestion** (when no preference, file path input only):
- `{article-dir}/imgs/` - Images subdirectory near article
- `{article-dir}/` - Same directory as article
- `cover-image/{topic-slug}/` - Independent directory
- Save as default - Remember this choice for future runs
### Step 2: Confirm Options ⚠️ ### Step 2: Confirm Options ⚠️
**Purpose**: Validate all 4 dimensions + aspect ratio. **Purpose**: Validate all 4 dimensions + aspect ratio.
@@ -615,9 +660,8 @@ Style notes: [key characteristics from style definition]
**Watermark Application** (if enabled in preferences): **Watermark Application** (if enabled in preferences):
Add to prompt: Add to prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position] Include a subtle watermark "[content]" positioned at [position].
with approximately [opacity*100]% visibility. The watermark should The watermark should be legible but not distracting from the main content.
be legible but not distracting from the main content.
``` ```
Reference: `references/config/watermark-guide.md` Reference: `references/config/watermark-guide.md`
@@ -15,7 +15,6 @@ watermark:
enabled: false enabled: false
content: "" content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right 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_type: null # hero|conceptual|typography|metaphor|scene|minimal or null for auto-select
@@ -52,7 +51,6 @@ custom_styles:
| `watermark.enabled` | bool | false | Enable watermark | | `watermark.enabled` | bool | false | Enable watermark |
| `watermark.content` | string | "" | Watermark text (@username or custom) | | `watermark.content` | string | "" | Watermark text (@username or custom) |
| `watermark.position` | enum | bottom-right | Position on image | | `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_type` | string | null | Type name or null for auto |
| `preferred_style` | string | null | Style name or null for auto | | `preferred_style` | string | null | Style name or null for auto |
| `preferred_text` | string | title-only | Text density level | | `preferred_text` | string | title-only | Text density level |
@@ -145,7 +143,6 @@ watermark:
enabled: true enabled: true
content: "myblog.com" content: "myblog.com"
position: bottom-right position: bottom-right
opacity: 0.5
preferred_type: conceptual preferred_type: conceptual
@@ -28,15 +28,6 @@ description: Watermark configuration guide for baoyu-cover-image
| `bottom-center` | Centered designs | Text-heavy bottom area | | `bottom-center` | Centered designs | Text-heavy bottom area |
| `top-right` | Bottom-heavy content | Title/header in top-right | | `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 ## Content Format
| Format | Example | Style | | Format | Example | Style |
@@ -51,16 +42,14 @@ description: Watermark configuration guide for baoyu-cover-image
1. **Consistency**: Use same watermark across all covers 1. **Consistency**: Use same watermark across all covers
2. **Legibility**: Ensure watermark readable on both light/dark areas 2. **Legibility**: Ensure watermark readable on both light/dark areas
3. **Size**: Keep subtle - should not distract from content 3. **Size**: Keep subtle - should not distract from content
4. **Contrast**: Adjust opacity based on cover background
## Prompt Integration ## Prompt Integration
When watermark is enabled, add to image generation prompt: When watermark is enabled, add to image generation prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position] Include a subtle watermark "[content]" positioned at [position].
with approximately [opacity*100]% visibility. The watermark should The watermark should be legible but not distracting from the main content.
be legible but not distracting from the main content.
``` ```
## Cover-Specific Considerations ## Cover-Specific Considerations
@@ -75,7 +64,6 @@ be legible but not distracting from the main content.
| Issue | Solution | | Issue | Solution |
|-------|----------| |-------|----------|
| Watermark invisible | Increase opacity or adjust position | | Watermark invisible | Adjust position or check contrast |
| Watermark too prominent | Decrease opacity (0.3-0.5) | | Watermark too prominent | Change position or reduce size |
| Watermark overlaps title | Change position or reduce title area | | Watermark overlaps title | Change position or reduce title area |
| Inconsistent appearance | Use fixed opacity/position in preferences |
+18 -1
View File
@@ -56,8 +56,25 @@ npx -y bun ${SKILL_DIR}/scripts/wechat-browser.ts --title "标题" --content "
### Article (文章) ### Article (文章)
Before posting, ask user to choose a theme using AskUserQuestion:
| Theme | Description |
|-------|-------------|
| `default` | 经典主题 - 传统排版,标题居中带底边,二级标题白字彩底 |
| `grace` | 优雅主题 - 文字阴影,圆角卡片,精致引用块 (by @brzhang) |
| `simple` | 简洁主题 - 现代极简风,不对称圆角,清爽留白 (by @okooo5km) |
Default: `default`. If user has already specified a theme, skip the question.
**Workflow**:
1. Generate HTML preview and print the full `htmlPath` from JSON output so user can click to preview:
```bash ```bash
npx -y bun ${SKILL_DIR}/scripts/wechat-article.ts --markdown article.md --theme grace npx -y bun ${SKILL_DIR}/scripts/md-to-wechat.ts article.md --theme <chosen-theme>
```
2. Post to WeChat:
```bash
npx -y bun ${SKILL_DIR}/scripts/wechat-article.ts --markdown article.md --theme <chosen-theme>
``` ```
## Detailed References ## Detailed References
@@ -53,7 +53,7 @@ Regular paragraph with **bold** and *italic*.
## Image Handling ## Image Handling
1. **Parse**: Images in markdown are replaced with `[[IMAGE_PLACEHOLDER_N]]` 1. **Parse**: Images in markdown are replaced with `WECHATIMGPH_N`
2. **Render**: HTML is generated with placeholders in text 2. **Render**: HTML is generated with placeholders in text
3. **Paste**: HTML content is pasted into WeChat editor 3. **Paste**: HTML content is pasted into WeChat editor
4. **Replace**: For each placeholder: 4. **Replace**: For each placeholder:
@@ -81,7 +81,7 @@ Claude:
3. Opens Chrome, navigates to WeChat editor 3. Opens Chrome, navigates to WeChat editor
4. Pastes HTML content 4. Pastes HTML content
5. For each image: 5. For each image:
- Selects [[IMAGE_PLACEHOLDER_1]] - Selects WECHATIMGPH_1
- Scrolls into view - Scrolls into view
- Presses Backspace to delete - Presses Backspace to delete
- Pastes image - Pastes image
@@ -3,6 +3,7 @@ import path from 'node:path';
import { mkdir, writeFile } from 'node:fs/promises'; import { mkdir, writeFile } from 'node:fs/promises';
import os from 'node:os'; import os from 'node:os';
import { createHash } from 'node:crypto'; import { createHash } from 'node:crypto';
import { fileURLToPath } from 'node:url';
import https from 'node:https'; import https from 'node:https';
import http from 'node:http'; import http from 'node:http';
import { spawnSync } from 'node:child_process'; import { spawnSync } from 'node:child_process';
@@ -111,17 +112,11 @@ function parseFrontmatter(content: string): { frontmatter: Record<string, string
return { frontmatter, body: match[2]! }; return { frontmatter, body: match[2]! };
} }
async function parseMarkdownForWechat( export async function convertMarkdown(markdownPath: string, options?: { title?: string; theme?: string }): Promise<ParsedResult> {
markdownPath: string,
options?: { title?: string; theme?: string; tempDir?: string },
): Promise<ParsedResult> {
const content = fs.readFileSync(markdownPath, 'utf-8');
const baseDir = path.dirname(markdownPath); const baseDir = path.dirname(markdownPath);
const tempDir = options?.tempDir ?? path.join(os.tmpdir(), 'wechat-article-images'); const content = fs.readFileSync(markdownPath, 'utf-8');
const theme = options?.theme ?? 'default'; const theme = options?.theme ?? 'default';
await mkdir(tempDir, { recursive: true });
const { frontmatter, body } = parseFrontmatter(content); const { frontmatter, body } = parseFrontmatter(content);
let title = options?.title ?? frontmatter.title ?? ''; let title = options?.title ?? frontmatter.title ?? '';
@@ -129,9 +124,9 @@ async function parseMarkdownForWechat(
const h1Match = body.match(/^#\s+(.+)$/m); const h1Match = body.match(/^#\s+(.+)$/m);
if (h1Match) title = h1Match[1]!; if (h1Match) title = h1Match[1]!;
} }
if (!title) title = path.basename(markdownPath, path.extname(markdownPath));
const author = frontmatter.author ?? ''; const author = frontmatter.author || '';
let summary = frontmatter.summary ?? frontmatter.description ?? ''; let summary = frontmatter.description || frontmatter.summary || '';
if (!summary) { if (!summary) {
const lines = body.split('\n'); const lines = body.split('\n');
@@ -161,20 +156,23 @@ async function parseMarkdownForWechat(
let imageCounter = 0; let imageCounter = 0;
const modifiedBody = body.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, src) => { const modifiedBody = body.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, src) => {
const placeholder = `[[IMAGE_PLACEHOLDER_${++imageCounter}]]`; const placeholder = `WECHATIMGPH_${++imageCounter}`;
images.push({ src, placeholder }); images.push({ src, placeholder });
return placeholder; return placeholder;
}); });
const modifiedMarkdown = `---\n${Object.entries(frontmatter).map(([k, v]) => `${k}: ${v}`).join('\n')}\n---\n${modifiedBody}`; const modifiedMarkdown = `---\n${Object.entries(frontmatter).map(([k, v]) => `${k}: ${v}`).join('\n')}\n---\n${modifiedBody}`;
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-article-images-'));
const tempMdPath = path.join(tempDir, 'temp-article.md'); const tempMdPath = path.join(tempDir, 'temp-article.md');
await writeFile(tempMdPath, modifiedMarkdown, 'utf-8'); await writeFile(tempMdPath, modifiedMarkdown, 'utf-8');
const scriptDir = path.dirname(new URL(import.meta.url).pathname); const __filename = fileURLToPath(import.meta.url);
const renderScript = path.join(scriptDir, 'md', 'render.ts'); const __dirname = path.dirname(__filename);
const renderScript = path.join(__dirname, 'md', 'render.ts');
console.error(`[md-to-wechat] Rendering markdown with theme: ${theme}`); console.error(`[md-to-wechat] Rendering markdown with theme: ${theme}`);
const result = spawnSync('npx', ['-y', 'bun', renderScript, tempMdPath, '--theme', theme], { const result = spawnSync('npx', ['-y', 'bun', renderScript, tempMdPath, '--theme', theme], {
stdio: ['inherit', 'pipe', 'pipe'], stdio: ['inherit', 'pipe', 'pipe'],
cwd: baseDir, cwd: baseDir,
@@ -226,8 +224,8 @@ Output JSON format:
"htmlPath": "/tmp/wechat-article-images/temp-article.html", "htmlPath": "/tmp/wechat-article-images/temp-article.html",
"contentImages": [ "contentImages": [
{ {
"placeholder": "[[IMAGE_PLACEHOLDER_1]]", "placeholder": "WECHATIMGPH_1",
"localPath": "/tmp/wechat-article-images/img.png", "localPath": "/tmp/wechat-image/img.png",
"originalPath": "imgs/image.png" "originalPath": "imgs/image.png"
} }
] ]
@@ -262,7 +260,7 @@ async function main(): Promise<void> {
} }
if (!markdownPath) { if (!markdownPath) {
console.error('Error: Markdown file path required'); console.error('Error: Markdown file path is required');
process.exit(1); process.exit(1);
} }
@@ -271,7 +269,7 @@ async function main(): Promise<void> {
process.exit(1); process.exit(1);
} }
const result = await parseMarkdownForWechat(markdownPath, { title, theme }); const result = await convertMarkdown(markdownPath, { title, theme });
console.log(JSON.stringify(result, null, 2)); console.log(JSON.stringify(result, null, 2));
} }
@@ -2,6 +2,7 @@ import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { spawnSync } from 'node:child_process'; import { spawnSync } from 'node:child_process';
import process from 'node:process'; import process from 'node:process';
import { fileURLToPath } from 'node:url';
import { launchChrome, getPageSession, waitForNewTab, clickElement, typeText, evaluate, sleep, type ChromeSession, type CdpConnection } from './cdp.ts'; import { launchChrome, getPageSession, waitForNewTab, clickElement, typeText, evaluate, sleep, type ChromeSession, type CdpConnection } from './cdp.ts';
const WECHAT_URL = 'https://mp.weixin.qq.com/'; const WECHAT_URL = 'https://mp.weixin.qq.com/';
@@ -65,8 +66,9 @@ async function clickMenuByText(session: ChromeSession, text: string): Promise<vo
} }
async function copyImageToClipboard(imagePath: string): Promise<void> { async function copyImageToClipboard(imagePath: string): Promise<void> {
const scriptDir = path.dirname(new URL(import.meta.url).pathname); const __filename = fileURLToPath(import.meta.url);
const copyScript = path.join(scriptDir, './copy-to-clipboard.ts'); const __dirname = path.dirname(__filename);
const copyScript = path.join(__dirname, './copy-to-clipboard.ts');
const result = spawnSync('npx', ['-y', 'bun', copyScript, 'image', imagePath], { stdio: 'inherit' }); const result = spawnSync('npx', ['-y', 'bun', copyScript, 'image', imagePath], { stdio: 'inherit' });
if (result.status !== 0) throw new Error(`Failed to copy image: ${imagePath}`); if (result.status !== 0) throw new Error(`Failed to copy image: ${imagePath}`);
} }
@@ -78,6 +80,30 @@ async function pasteInEditor(session: ChromeSession): Promise<void> {
await session.cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'v', code: 'KeyV', modifiers, windowsVirtualKeyCode: 86 }, { sessionId: session.sessionId }); await session.cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'v', code: 'KeyV', modifiers, windowsVirtualKeyCode: 86 }, { sessionId: session.sessionId });
} }
async function sendCopy(cdp?: CdpConnection, sessionId?: string): Promise<void> {
if (process.platform === 'darwin') {
spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "c" using command down']);
} else if (process.platform === 'linux') {
spawnSync('xdotool', ['key', 'ctrl+c']);
} else if (cdp && sessionId) {
await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'c', code: 'KeyC', modifiers: 2, windowsVirtualKeyCode: 67 }, { sessionId });
await sleep(50);
await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'c', code: 'KeyC', modifiers: 2, windowsVirtualKeyCode: 67 }, { sessionId });
}
}
async function sendPaste(cdp?: CdpConnection, sessionId?: string): Promise<void> {
if (process.platform === 'darwin') {
spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "v" using command down']);
} else if (process.platform === 'linux') {
spawnSync('xdotool', ['key', 'ctrl+v']);
} else if (cdp && sessionId) {
await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'v', code: 'KeyV', modifiers: 2, windowsVirtualKeyCode: 86 }, { sessionId });
await sleep(50);
await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'v', code: 'KeyV', modifiers: 2, windowsVirtualKeyCode: 86 }, { sessionId });
}
}
async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Promise<void> { async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Promise<void> {
const absolutePath = path.isAbsolute(htmlFilePath) ? htmlFilePath : path.resolve(process.cwd(), htmlFilePath); const absolutePath = path.isAbsolute(htmlFilePath) ? htmlFilePath : path.resolve(process.cwd(), htmlFilePath);
const fileUrl = `file://${absolutePath}`; const fileUrl = `file://${absolutePath}`;
@@ -108,30 +134,24 @@ async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Pr
}, { sessionId }); }, { sessionId });
await sleep(300); await sleep(300);
console.log('[wechat] Copying with system Cmd+C...'); console.log('[wechat] Copying content...');
if (process.platform === 'darwin') { await sendCopy(cdp, sessionId);
spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "c" using command down']);
} else {
spawnSync('xdotool', ['key', 'ctrl+c']);
}
await sleep(1000); await sleep(1000);
console.log('[wechat] Closing HTML tab...'); console.log('[wechat] Closing HTML tab...');
await cdp.send('Target.closeTarget', { targetId }); await cdp.send('Target.closeTarget', { targetId });
} }
async function pasteFromClipboardInEditor(): Promise<void> { async function pasteFromClipboardInEditor(session: ChromeSession): Promise<void> {
if (process.platform === 'darwin') { console.log('[wechat] Pasting content...');
spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "v" using command down']); await sendPaste(session.cdp, session.sessionId);
} else {
spawnSync('xdotool', ['key', 'ctrl+v']);
}
await sleep(1000); await sleep(1000);
} }
async function parseMarkdownWithPlaceholders(markdownPath: string, theme?: string): Promise<{ title: string; author: string; summary: string; htmlPath: string; contentImages: ImageInfo[] }> { async function parseMarkdownWithPlaceholders(markdownPath: string, theme?: string): Promise<{ title: string; author: string; summary: string; htmlPath: string; contentImages: ImageInfo[] }> {
const scriptDir = path.dirname(new URL(import.meta.url).pathname); const __filename = fileURLToPath(import.meta.url);
const mdToWechatScript = path.join(scriptDir, 'md-to-wechat.ts'); const __dirname = path.dirname(__filename);
const mdToWechatScript = path.join(__dirname, 'md-to-wechat.ts');
const args = ['-y', 'bun', mdToWechatScript, markdownPath]; const args = ['-y', 'bun', mdToWechatScript, markdownPath];
if (theme) args.push('--theme', theme); if (theme) args.push('--theme', theme);
@@ -295,6 +315,10 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
console.log('[wechat] Clicking on editor...'); console.log('[wechat] Clicking on editor...');
await clickElement(session, '.ProseMirror'); await clickElement(session, '.ProseMirror');
await sleep(1000);
console.log('[wechat] Ensuring editor focus...');
await clickElement(session, '.ProseMirror');
await sleep(500); await sleep(500);
if (effectiveHtmlFile && fs.existsSync(effectiveHtmlFile)) { if (effectiveHtmlFile && fs.existsSync(effectiveHtmlFile)) {
@@ -302,7 +326,7 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
await copyHtmlFromBrowser(cdp, effectiveHtmlFile); await copyHtmlFromBrowser(cdp, effectiveHtmlFile);
await sleep(500); await sleep(500);
console.log('[wechat] Pasting into editor...'); console.log('[wechat] Pasting into editor...');
await pasteFromClipboardInEditor(); await pasteFromClipboardInEditor(session);
await sleep(3000); await sleep(3000);
if (contentImages.length > 0) { if (contentImages.length > 0) {
@@ -328,7 +352,7 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
await sleep(200); await sleep(200);
console.log('[wechat] Pasting image...'); console.log('[wechat] Pasting image...');
await pasteFromClipboardInEditor(); await pasteFromClipboardInEditor(session);
await sleep(3000); await sleep(3000);
} }
console.log('[wechat] All images inserted.'); console.log('[wechat] All images inserted.');
@@ -67,7 +67,7 @@ Code blocks become blockquotes (X doesn't support code)
1. **Cover Image**: First image or `cover_image` from frontmatter 1. **Cover Image**: First image or `cover_image` from frontmatter
2. **Remote Images**: Automatically downloaded to temp directory 2. **Remote Images**: Automatically downloaded to temp directory
3. **Placeholders**: Images in content use `[[IMAGE_PLACEHOLDER_N]]` format 3. **Placeholders**: Images in content use `XIMGPH_N` format
4. **Insertion**: Placeholders are found, selected, and replaced with actual images 4. **Insertion**: Placeholders are found, selected, and replaced with actual images
## Markdown to HTML Script ## Markdown to HTML Script
@@ -92,7 +92,7 @@ JSON output:
"coverImage": "/path/to/cover.jpg", "coverImage": "/path/to/cover.jpg",
"contentImages": [ "contentImages": [
{ {
"placeholder": "[[IMAGE_PLACEHOLDER_1]]", "placeholder": "XIMGPH_1",
"localPath": "/tmp/x-article-images/img.png", "localPath": "/tmp/x-article-images/img.png",
"blockIndex": 5 "blockIndex": 5
} }
+5 -5
View File
@@ -280,7 +280,7 @@ export async function parseMarkdown(
let imageCounter = 0; let imageCounter = 0;
const { html, totalBlocks } = convertMarkdownToHtml(body, (src, alt) => { const { html, totalBlocks } = convertMarkdownToHtml(body, (src, alt) => {
const placeholder = `[[IMAGE_PLACEHOLDER_${++imageCounter}]]`; const placeholder = `XIMGPH_${++imageCounter}`;
const currentBlockIndex = images.length; // Will be set properly after HTML generation const currentBlockIndex = images.length; // Will be set properly after HTML generation
images.push({ src, alt, blockIndex: -1 }); // blockIndex set later images.push({ src, alt, blockIndex: -1 }); // blockIndex set later
@@ -292,7 +292,7 @@ export async function parseMarkdown(
let blockIdx = 0; let blockIdx = 0;
for (const line of htmlLines) { for (const line of htmlLines) {
for (let i = 0; i < images.length; i++) { for (let i = 0; i < images.length; i++) {
const placeholder = `[[IMAGE_PLACEHOLDER_${i + 1}]]`; const placeholder = `XIMGPH_${i + 1}`;
if (line.includes(placeholder)) { if (line.includes(placeholder)) {
images[i]!.blockIndex = blockIdx; images[i]!.blockIndex = blockIdx;
} }
@@ -312,7 +312,7 @@ export async function parseMarkdown(
// First image becomes cover if no cover specified // First image becomes cover if no cover specified
if (isFirstImage && !coverImagePath) { if (isFirstImage && !coverImagePath) {
coverImagePath = localPath; coverImagePath = localPath;
coverPlaceholder = `[[IMAGE_PLACEHOLDER_${i + 1}]]`; coverPlaceholder = `XIMGPH_${i + 1}`;
isFirstImage = false; isFirstImage = false;
// Don't add to contentImages, it's the cover // Don't add to contentImages, it's the cover
continue; continue;
@@ -320,7 +320,7 @@ export async function parseMarkdown(
isFirstImage = false; isFirstImage = false;
contentImages.push({ contentImages.push({
placeholder: `[[IMAGE_PLACEHOLDER_${i + 1}]]`, placeholder: `XIMGPH_${i + 1}`,
localPath, localPath,
originalPath: img.src, originalPath: img.src,
blockIndex: img.blockIndex, blockIndex: img.blockIndex,
@@ -331,7 +331,7 @@ export async function parseMarkdown(
let finalHtml = html; let finalHtml = html;
if (coverPlaceholder) { if (coverPlaceholder) {
// Remove the placeholder and its containing <p> tag // Remove the placeholder and its containing <p> tag
finalHtml = finalHtml.replace(new RegExp(`<p>${coverPlaceholder.replace(/[[\]]/g, '\\$&')}</p>\\n?`, 'g'), ''); finalHtml = finalHtml.replace(new RegExp(`<p>${coverPlaceholder}</p>\\n?`, 'g'), '');
} }
// Resolve cover image path // Resolve cover image path
+2 -3
View File
@@ -359,9 +359,8 @@ With confirmed outline + style + layout:
**Watermark Application** (if enabled in preferences): **Watermark Application** (if enabled in preferences):
Add to each image generation prompt: Add to each image generation prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position] Include a subtle watermark "[content]" positioned at [position].
with approximately [opacity*100]% visibility. The watermark should The watermark should be legible but not distracting from the main content.
be legible but not distracting from the main content.
``` ```
Reference: `references/config/watermark-guide.md` Reference: `references/config/watermark-guide.md`
@@ -15,7 +15,6 @@ watermark:
enabled: false enabled: false
content: "" content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
opacity: 0.7 # 0.1-1.0
preferred_style: preferred_style:
name: null # Built-in or custom style name name: null # Built-in or custom style name
@@ -46,7 +45,6 @@ custom_styles:
| `watermark.enabled` | bool | false | Enable watermark | | `watermark.enabled` | bool | false | Enable watermark |
| `watermark.content` | string | "" | Watermark text (@username or custom) | | `watermark.content` | string | "" | Watermark text (@username or custom) |
| `watermark.position` | enum | bottom-right | Position on image | | `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.name` | string | null | Style name or null |
| `preferred_style.description` | string | "" | Custom notes/override | | `preferred_style.description` | string | "" | Custom notes/override |
| `preferred_layout` | string | null | Layout preference or null | | `preferred_layout` | string | null | Layout preference or null |
@@ -97,7 +95,6 @@ watermark:
enabled: true enabled: true
content: "@myxhsaccount" content: "@myxhsaccount"
position: bottom-right position: bottom-right
opacity: 0.5
preferred_style: preferred_style:
name: notion name: notion
@@ -28,15 +28,6 @@ description: Watermark configuration guide for baoyu-xhs-images
| `bottom-center` | Centered designs | Text-heavy bottom area | | `bottom-center` | Centered designs | Text-heavy bottom area |
| `top-right` | Bottom-heavy content | Title/header in top-right | | `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 ## Content Format
| Format | Example | Style | | Format | Example | Style |
@@ -51,23 +42,21 @@ description: Watermark configuration guide for baoyu-xhs-images
1. **Consistency**: Use same watermark across all images in series 1. **Consistency**: Use same watermark across all images in series
2. **Legibility**: Ensure watermark readable on both light/dark areas 2. **Legibility**: Ensure watermark readable on both light/dark areas
3. **Size**: Keep subtle - should not distract from content 3. **Size**: Keep subtle - should not distract from content
4. **Contrast**: Opacity may need adjustment per image background
## Prompt Integration ## Prompt Integration
When watermark is enabled, add to image generation prompt: When watermark is enabled, add to image generation prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position] Include a subtle watermark "[content]" positioned at [position].
with approximately [opacity*100]% visibility. The watermark should The watermark should be legible but not distracting from the main content.
be legible but not distracting from the main content.
``` ```
## Common Issues ## Common Issues
| Issue | Solution | | Issue | Solution |
|-------|----------| |-------|----------|
| Watermark invisible | Increase opacity or adjust position | | Watermark invisible | Adjust position or check contrast |
| Watermark too prominent | Decrease opacity (0.3-0.5) | | Watermark too prominent | Change position or reduce size |
| Watermark overlaps content | Change position | | Watermark overlaps content | Change position |
| Inconsistent across images | Use session ID for consistency | | Inconsistent across images | Use session ID for consistency |