mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-17 16:09:47 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b12ae7374d | |||
| 1afa6f5a58 | |||
| 977598d5ae | |||
| eb738aa61a | |||
| e9a030f917 |
@@ -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.4"
|
"version": "1.22.0"
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
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
|
## 1.21.4 - 2026-01-25
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|||||||
@@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
[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
|
## 1.21.4 - 2026-01-25
|
||||||
|
|
||||||
### 修复
|
### 修复
|
||||||
|
|||||||
@@ -85,12 +85,14 @@ Check `default_output_dir` in preferences:
|
|||||||
| Preference Value | Action |
|
| Preference Value | Action |
|
||||||
|------------------|--------|
|
|------------------|--------|
|
||||||
| `same-dir` | Use `{article-dir}/`, display "Output: {path}" |
|
| `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}" |
|
| `illustrations-subdir` | Use `{article-dir}/illustrations/`, display "Output: {path}" |
|
||||||
| `independent` | Use `illustrations/{topic-slug}/`, display "Output: {path}" |
|
| `independent` | Use `illustrations/{topic-slug}/`, display "Output: {path}" |
|
||||||
| Not configured | **MUST** ask with AskUserQuestion ↓ |
|
| Not configured | **MUST** ask with AskUserQuestion ↓ |
|
||||||
|
|
||||||
**AskUserQuestion** (when no preference):
|
**AskUserQuestion** (when no preference):
|
||||||
- `{article-dir}/` - Same directory as article
|
- `{article-dir}/` - Same directory as article
|
||||||
|
- `{article-dir}/imgs/` - Images subdirectory
|
||||||
- `{article-dir}/illustrations/` - Illustrations subdirectory (Recommended)
|
- `{article-dir}/illustrations/` - Illustrations subdirectory (Recommended)
|
||||||
- `illustrations/{topic-slug}/` - Independent directory
|
- `illustrations/{topic-slug}/` - Independent directory
|
||||||
- Save as default - Remember this choice for future runs
|
- Save as default - Remember this choice for future runs
|
||||||
@@ -175,11 +177,22 @@ test -f "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "user"
|
|||||||
- balanced (3-5) (Recommended) - Major sections
|
- balanced (3-5) (Recommended) - Major sections
|
||||||
- rich (6+) - Comprehensive support
|
- rich (6+) - Comprehensive support
|
||||||
|
|
||||||
**Q3: Style**
|
**Q3: Style** (ALWAYS ask, even with preferred_style in EXTEND.md)
|
||||||
Based on Type, suggest compatible styles from matrix:
|
|
||||||
- [Best compatible] (Recommended)
|
If EXTEND.md has `preferred_style`:
|
||||||
- [Other ✓✓ styles]
|
- [Custom style name + brief description] (Recommended)
|
||||||
- [Other ✓ styles]
|
- [Top compatible built-in style 1]
|
||||||
|
- [Top compatible built-in style 2]
|
||||||
|
- [Top compatible built-in style 3]
|
||||||
|
|
||||||
|
If no `preferred_style`:
|
||||||
|
- [Best compatible from matrix] (Recommended)
|
||||||
|
- [Other ✓✓ style 1]
|
||||||
|
- [Other ✓✓ style 2]
|
||||||
|
- [Other ✓ style]
|
||||||
|
|
||||||
|
Style selection based on Type × Style compatibility matrix (references/styles.md).
|
||||||
|
Full specs: `references/styles/<style>.md`
|
||||||
|
|
||||||
**Q4** (only if source ≠ user language):
|
**Q4** (only if source ≠ user language):
|
||||||
- Language: Source / User language
|
- Language: Source / User language
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export async function convertMarkdown(markdownPath: string, options?: { title?:
|
|||||||
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;
|
||||||
});
|
});
|
||||||
@@ -224,7 +224,7 @@ 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-image/img.png",
|
"localPath": "/tmp/wechat-image/img.png",
|
||||||
"originalPath": "imgs/image.png"
|
"originalPath": "imgs/image.png"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,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}`;
|
||||||
@@ -110,23 +134,8 @@ async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Pr
|
|||||||
}, { sessionId });
|
}, { sessionId });
|
||||||
await sleep(300);
|
await sleep(300);
|
||||||
|
|
||||||
console.log('[wechat] Copying with CDP keyboard event...');
|
console.log('[wechat] Copying content...');
|
||||||
const modifiers = process.platform === 'darwin' ? 4 : 2;
|
await sendCopy(cdp, sessionId);
|
||||||
await cdp.send('Input.dispatchKeyEvent', {
|
|
||||||
type: 'keyDown',
|
|
||||||
key: 'c',
|
|
||||||
code: 'KeyC',
|
|
||||||
modifiers,
|
|
||||||
windowsVirtualKeyCode: 67
|
|
||||||
}, { sessionId });
|
|
||||||
await sleep(50);
|
|
||||||
await cdp.send('Input.dispatchKeyEvent', {
|
|
||||||
type: 'keyUp',
|
|
||||||
key: 'c',
|
|
||||||
code: 'KeyC',
|
|
||||||
modifiers,
|
|
||||||
windowsVirtualKeyCode: 67
|
|
||||||
}, { sessionId });
|
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
|
|
||||||
console.log('[wechat] Closing HTML tab...');
|
console.log('[wechat] Closing HTML tab...');
|
||||||
@@ -134,23 +143,8 @@ async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function pasteFromClipboardInEditor(session: ChromeSession): Promise<void> {
|
async function pasteFromClipboardInEditor(session: ChromeSession): Promise<void> {
|
||||||
console.log('[wechat] Pasting with CDP keyboard event...');
|
console.log('[wechat] Pasting content...');
|
||||||
const modifiers = process.platform === 'darwin' ? 4 : 2;
|
await sendPaste(session.cdp, session.sessionId);
|
||||||
await session.cdp.send('Input.dispatchKeyEvent', {
|
|
||||||
type: 'keyDown',
|
|
||||||
key: 'v',
|
|
||||||
code: 'KeyV',
|
|
||||||
modifiers,
|
|
||||||
windowsVirtualKeyCode: 86
|
|
||||||
}, { sessionId: session.sessionId });
|
|
||||||
await sleep(50);
|
|
||||||
await session.cdp.send('Input.dispatchKeyEvent', {
|
|
||||||
type: 'keyUp',
|
|
||||||
key: 'v',
|
|
||||||
code: 'KeyV',
|
|
||||||
modifiers,
|
|
||||||
windowsVirtualKeyCode: 86
|
|
||||||
}, { sessionId: session.sessionId });
|
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user