myaidev-method 0.2.12 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +40 -0
- package/CHANGELOG.md +96 -0
- package/CONTENT_CREATION_GUIDE.md +3399 -0
- package/DEVELOPER_USE_CASES.md +2085 -0
- package/README.md +209 -2
- package/VISUAL_GENERATION_FILE_ORGANIZATION.md +105 -0
- package/bin/cli.js +46 -0
- package/package.json +18 -3
- package/src/lib/asset-management.js +532 -0
- package/src/lib/visual-config-utils.js +424 -0
- package/src/lib/visual-generation-utils.js +880 -0
- package/src/scripts/configure-visual-apis.js +413 -0
- package/src/scripts/generate-visual-cli.js +279 -0
- package/src/templates/claude/agents/content-production-coordinator.md +111 -0
- package/src/templates/claude/agents/content-writer.md +209 -4
- package/src/templates/claude/agents/proprietary-content-verifier.md +96 -0
- package/src/templates/claude/agents/visual-content-generator.md +520 -0
- package/src/templates/claude/commands/myai-content-writer.md +33 -8
- package/src/templates/claude/commands/myai-coordinate-content.md +136 -0
- package/src/templates/claude/commands/myai-generate-visual.md +318 -0
- package/src/templates/codex/commands/myai-generate-visual.md +307 -0
- package/src/templates/gemini/commands/myai-generate-visual.md +200 -0
- package/.claude/CLAUDE.md +0 -52
- package/.claude/agents/content-writer.md +0 -155
- package/.claude/agents/wordpress-admin.md +0 -271
- package/.claude/commands/myai-configure.md +0 -44
- package/.claude/commands/myai-content-writer.md +0 -78
- package/.claude/commands/myai-wordpress-publish.md +0 -120
- package/.claude/mcp/gutenberg-converter.js +0 -447
- package/.claude/mcp/mcp-config.json +0 -184
- package/.claude/mcp/wordpress-server-simple.js +0 -182
- package/.claude/settings.local.json +0 -12
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: myai-configure
|
|
3
|
-
description: Configure MyAI Method settings including WordPress credentials
|
|
4
|
-
tools: Read, Write, Edit
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
Configure MyAI Method settings based on the parameter: $ARGUMENTS
|
|
8
|
-
|
|
9
|
-
## Available Configurations
|
|
10
|
-
|
|
11
|
-
### wordpress
|
|
12
|
-
Set up WordPress connection:
|
|
13
|
-
1. Prompt for WordPress site URL
|
|
14
|
-
2. Prompt for username
|
|
15
|
-
3. Guide through Application Password creation
|
|
16
|
-
4. Ask if Gutenberg block format should be default (yes/no)
|
|
17
|
-
5. Save to `.env` file in project root
|
|
18
|
-
|
|
19
|
-
### defaults
|
|
20
|
-
Configure default content settings:
|
|
21
|
-
- Default word count
|
|
22
|
-
- Default post status
|
|
23
|
-
- Default tone
|
|
24
|
-
- Default audience
|
|
25
|
-
|
|
26
|
-
### agents
|
|
27
|
-
List and manage available agents:
|
|
28
|
-
- Show installed agents
|
|
29
|
-
- Edit agent configurations
|
|
30
|
-
- Test agent functionality
|
|
31
|
-
|
|
32
|
-
## Process
|
|
33
|
-
|
|
34
|
-
1. Parse $ARGUMENTS to determine configuration type
|
|
35
|
-
2. Interactive prompts for required values
|
|
36
|
-
3. Validate configuration
|
|
37
|
-
4. Save to appropriate location (.env or .claude/config/)
|
|
38
|
-
5. Confirm successful configuration
|
|
39
|
-
|
|
40
|
-
## Security
|
|
41
|
-
|
|
42
|
-
- Never display passwords in plain text
|
|
43
|
-
- Use environment variables for sensitive data
|
|
44
|
-
- Validate URLs and credentials before saving
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: myai-content-writer
|
|
3
|
-
description: Professional content writer for SEO-optimized articles and blog posts
|
|
4
|
-
tools: Read, Write, Edit, WebSearch, WebFetch, Task
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
You are a professional content writer specializing in creating high-quality, engaging, and SEO-optimized content.
|
|
8
|
-
|
|
9
|
-
## Task
|
|
10
|
-
Create comprehensive, well-researched content based on the provided topic: $ARGUMENTS
|
|
11
|
-
|
|
12
|
-
## Core Competencies
|
|
13
|
-
|
|
14
|
-
1. **Writing Excellence**
|
|
15
|
-
- Clear, concise, and engaging prose
|
|
16
|
-
- Proper grammar, spelling, and punctuation
|
|
17
|
-
- Varied sentence structure and rhythm
|
|
18
|
-
- Active voice preference
|
|
19
|
-
- Storytelling when appropriate
|
|
20
|
-
|
|
21
|
-
2. **SEO Optimization**
|
|
22
|
-
- Natural keyword integration
|
|
23
|
-
- Proper heading hierarchy (H1, H2, H3)
|
|
24
|
-
- Meta descriptions (150-160 characters)
|
|
25
|
-
- Internal and external linking suggestions
|
|
26
|
-
- URL-friendly slugs
|
|
27
|
-
|
|
28
|
-
3. **Content Structure**
|
|
29
|
-
- Compelling introductions with hooks
|
|
30
|
-
- Logical flow and transitions
|
|
31
|
-
- Scannable formatting (bullets, lists, short paragraphs)
|
|
32
|
-
- Strong conclusions with CTAs when appropriate
|
|
33
|
-
|
|
34
|
-
## Writing Process
|
|
35
|
-
|
|
36
|
-
1. **Understanding**: Analyze the topic, audience, and goals from $ARGUMENTS
|
|
37
|
-
2. **Research**: Use WebSearch to gather current, relevant information
|
|
38
|
-
3. **Planning**: Create a detailed outline with logical structure
|
|
39
|
-
4. **Writing**: Craft the content following SEO best practices
|
|
40
|
-
5. **Optimization**: Review for clarity, accuracy, and SEO
|
|
41
|
-
6. **Formatting**: Ensure proper Markdown formatting
|
|
42
|
-
|
|
43
|
-
## Output Requirements
|
|
44
|
-
|
|
45
|
-
Save the content as a markdown file with the following structure:
|
|
46
|
-
|
|
47
|
-
```markdown
|
|
48
|
-
---
|
|
49
|
-
title: [Compelling, keyword-rich title]
|
|
50
|
-
meta_description: [150-160 character summary]
|
|
51
|
-
slug: [url-friendly-version]
|
|
52
|
-
tags: [relevant, topic, tags]
|
|
53
|
-
category: [Primary category]
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
# [Title]
|
|
57
|
-
|
|
58
|
-
[Content with proper heading hierarchy]
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Parameters Supported
|
|
62
|
-
|
|
63
|
-
- Topic (required): Main subject matter
|
|
64
|
-
- Word count: Target length (default: 800)
|
|
65
|
-
- Tone: professional, casual, technical, conversational, academic
|
|
66
|
-
- Audience: Target reader demographic
|
|
67
|
-
- SEO keywords: Primary and secondary keywords
|
|
68
|
-
- Publish to WordPress: Auto-publish as draft (true/false)
|
|
69
|
-
|
|
70
|
-
## WordPress Integration
|
|
71
|
-
|
|
72
|
-
If publish_to_wordpress is requested:
|
|
73
|
-
1. Save the content locally first
|
|
74
|
-
2. Check for WordPress configuration in environment variables
|
|
75
|
-
3. Use the WordPress MCP to create a draft post
|
|
76
|
-
4. Report the draft URL to the user
|
|
77
|
-
|
|
78
|
-
Always prioritize user value over keyword density. Write for humans first, search engines second.
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: myai-wordpress-publish
|
|
3
|
-
description: Publish markdown content to WordPress using enhanced MCP server with session management
|
|
4
|
-
tools: Read, Task, mcp__myaidev__wp_create_post, mcp__myaidev__wp_session_create, mcp__myaidev__wp_health_check
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
Publish the specified markdown file to WordPress using the enhanced MCP integration with session management and memory persistence.
|
|
8
|
-
|
|
9
|
-
## Task
|
|
10
|
-
Read and publish the file specified in $ARGUMENTS to WordPress using the enhanced MCP server.
|
|
11
|
-
|
|
12
|
-
## Enhanced Process
|
|
13
|
-
|
|
14
|
-
1. **Initialize WordPress session** using `mcp__myaidev__wp_session_create`
|
|
15
|
-
2. **Perform health check** with `mcp__myaidev__wp_health_check` to validate connectivity
|
|
16
|
-
3. **Read the markdown file** from the path provided in $ARGUMENTS
|
|
17
|
-
4. **Parse the frontmatter** to extract:
|
|
18
|
-
- title
|
|
19
|
-
- meta_description (use as excerpt)
|
|
20
|
-
- slug
|
|
21
|
-
- tags (convert to tag names/IDs)
|
|
22
|
-
- category (convert to category names/IDs)
|
|
23
|
-
- use_gutenberg (optional, inherits from environment)
|
|
24
|
-
- author (optional)
|
|
25
|
-
- comment_status (optional, default: "open")
|
|
26
|
-
- ping_status (optional, default: "open")
|
|
27
|
-
5. **Validate content** and prepare for publishing:
|
|
28
|
-
- Check required fields (title, content)
|
|
29
|
-
- Validate status value
|
|
30
|
-
- Prepare category and tag mappings
|
|
31
|
-
6. **Create the post** using `mcp__myaidev__wp_create_post` with:
|
|
32
|
-
- Enhanced error handling and validation
|
|
33
|
-
- Automatic Gutenberg conversion if enabled
|
|
34
|
-
- Session tracking for operation logging
|
|
35
|
-
- Memory storage for operation persistence
|
|
36
|
-
7. **Report comprehensive results** including:
|
|
37
|
-
- Post ID and URLs (public, edit)
|
|
38
|
-
- Session ID for tracking
|
|
39
|
-
- Format used (Classic or Gutenberg)
|
|
40
|
-
- Operation metrics (response time, warnings)
|
|
41
|
-
- Memory storage confirmation
|
|
42
|
-
|
|
43
|
-
## Parameters
|
|
44
|
-
|
|
45
|
-
- File path (required): Path to markdown file to publish
|
|
46
|
-
- --status: draft (default), publish, pending, private
|
|
47
|
-
- --gutenberg: Use Gutenberg block format (inherits from environment if not specified)
|
|
48
|
-
- --classic: Force classic editor format (overrides defaults)
|
|
49
|
-
- --batch: Process multiple files (experimental)
|
|
50
|
-
- --dry-run: Validate without actually publishing
|
|
51
|
-
- --session-id: Use existing session ID (optional)
|
|
52
|
-
|
|
53
|
-
## Enhanced Error Handling
|
|
54
|
-
|
|
55
|
-
**Configuration Issues:**
|
|
56
|
-
- Use enhanced health check to validate WordPress connectivity
|
|
57
|
-
- Provide detailed error messages from MCP server
|
|
58
|
-
- Guide user through `/myai-configure wordpress` setup if needed
|
|
59
|
-
|
|
60
|
-
**File Processing:**
|
|
61
|
-
- Validate file existence and readability before processing
|
|
62
|
-
- Report specific parsing errors for frontmatter
|
|
63
|
-
- Suggest corrections for invalid field values
|
|
64
|
-
|
|
65
|
-
**Publishing Errors:**
|
|
66
|
-
- Detailed API error reporting from enhanced MCP server
|
|
67
|
-
- Session-based error tracking and recovery
|
|
68
|
-
- Automatic retry mechanisms for transient failures
|
|
69
|
-
|
|
70
|
-
## Enhanced Success Response
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
✅ Successfully published to WordPress via Enhanced MCP Server!
|
|
74
|
-
|
|
75
|
-
📄 Post Details:
|
|
76
|
-
- Post ID: [id]
|
|
77
|
-
- Title: [title]
|
|
78
|
-
- Status: [draft/published]
|
|
79
|
-
- Format: [Classic/Gutenberg]
|
|
80
|
-
|
|
81
|
-
🔗 URLs:
|
|
82
|
-
- Preview: [preview_url]
|
|
83
|
-
- Edit: [admin_edit_url]
|
|
84
|
-
|
|
85
|
-
📊 Operation Details:
|
|
86
|
-
- Session ID: [session_id]
|
|
87
|
-
- Response Time: [duration]ms
|
|
88
|
-
- Memory Stored: [stored_in_memory]
|
|
89
|
-
- Server Version: 2.0.0
|
|
90
|
-
|
|
91
|
-
📈 Next Steps:
|
|
92
|
-
- View session status: Use session ID to track operations
|
|
93
|
-
- Edit in WordPress: Click edit URL above
|
|
94
|
-
- Publish live: Change status from draft to publish if needed
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## MCP Tools Integration
|
|
98
|
-
|
|
99
|
-
This command leverages the enhanced WordPress MCP server tools:
|
|
100
|
-
|
|
101
|
-
1. **wp_session_create**: Initialize tracking session
|
|
102
|
-
2. **wp_health_check**: Validate WordPress connectivity
|
|
103
|
-
3. **wp_create_post**: Create post with enhanced features:
|
|
104
|
-
- Automatic Gutenberg conversion
|
|
105
|
-
- Memory persistence
|
|
106
|
-
- Session logging
|
|
107
|
-
- Comprehensive validation
|
|
108
|
-
4. **wp_memory_store**: Store operation results for later reference
|
|
109
|
-
|
|
110
|
-
## Troubleshooting
|
|
111
|
-
|
|
112
|
-
**MCP Server Issues:**
|
|
113
|
-
- Ensure WordPress MCP server is running
|
|
114
|
-
- Check environment variables are set correctly
|
|
115
|
-
- Use health check tool to diagnose connectivity
|
|
116
|
-
|
|
117
|
-
**Publishing Problems:**
|
|
118
|
-
- Check session status for detailed error logs
|
|
119
|
-
- Use dry-run mode to validate before publishing
|
|
120
|
-
- Review memory stored data for operation history
|
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gutenberg Block Converter
|
|
3
|
-
* Converts HTML/Markdown content to WordPress Gutenberg block format
|
|
4
|
-
* Following WordPress Block Editor Handbook specifications
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export class GutenbergConverter {
|
|
8
|
-
/**
|
|
9
|
-
* Convert HTML content to Gutenberg blocks
|
|
10
|
-
* @param {string} html - HTML content to convert
|
|
11
|
-
* @returns {string} - Gutenberg block formatted content
|
|
12
|
-
*/
|
|
13
|
-
static htmlToGutenberg(html) {
|
|
14
|
-
// Parse HTML and convert to blocks
|
|
15
|
-
let gutenbergContent = '';
|
|
16
|
-
|
|
17
|
-
// Split content into sections for processing
|
|
18
|
-
const sections = this.parseHTMLSections(html);
|
|
19
|
-
|
|
20
|
-
sections.forEach(section => {
|
|
21
|
-
const block = this.createBlock(section);
|
|
22
|
-
if (block) {
|
|
23
|
-
gutenbergContent += block + '\n\n';
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
return gutenbergContent.trim();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Parse HTML into sections for block conversion
|
|
32
|
-
*/
|
|
33
|
-
static parseHTMLSections(html) {
|
|
34
|
-
const sections = [];
|
|
35
|
-
|
|
36
|
-
// Remove excess whitespace and normalize
|
|
37
|
-
const normalizedHtml = html.replace(/\n\s*\n/g, '\n').trim();
|
|
38
|
-
|
|
39
|
-
// Regular expressions for different HTML elements
|
|
40
|
-
const patterns = {
|
|
41
|
-
heading: /<h([1-6])(?:\s[^>]*)?>(.+?)<\/h\1>/gi,
|
|
42
|
-
paragraph: /<p(?:\s[^>]*)?>(.+?)<\/p>/gi,
|
|
43
|
-
list: /<(ul|ol)(?:\s[^>]*)?>(.+?)<\/\1>/gis,
|
|
44
|
-
blockquote: /<blockquote(?:\s[^>]*)?>(.+?)<\/blockquote>/gis,
|
|
45
|
-
pre: /<pre(?:\s[^>]*)?><code(?:\s[^>]*)?>(.+?)<\/code><\/pre>/gis,
|
|
46
|
-
image: /<img\s+([^>]+)>/gi,
|
|
47
|
-
hr: /<hr(?:\s[^>]*)?>/gi
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// Process the HTML string sequentially
|
|
51
|
-
let lastIndex = 0;
|
|
52
|
-
const processedParts = [];
|
|
53
|
-
|
|
54
|
-
// Create a combined pattern to find all blocks
|
|
55
|
-
const combinedPattern = new RegExp(
|
|
56
|
-
'(' +
|
|
57
|
-
'<h[1-6](?:\\s[^>]*)?>.*?</h[1-6]>|' +
|
|
58
|
-
'<p(?:\\s[^>]*)?>.*?</p>|' +
|
|
59
|
-
'<(?:ul|ol)(?:\\s[^>]*)?>.*?</(?:ul|ol)>|' +
|
|
60
|
-
'<blockquote(?:\\s[^>]*)?>.*?</blockquote>|' +
|
|
61
|
-
'<pre(?:\\s[^>]*)?><code(?:\\s[^>]*)?>.*?</code></pre>|' +
|
|
62
|
-
'<img\\s+[^>]+>|' +
|
|
63
|
-
'<hr(?:\\s[^>]*)?>' +
|
|
64
|
-
')',
|
|
65
|
-
'gis'
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
let match;
|
|
69
|
-
while ((match = combinedPattern.exec(normalizedHtml)) !== null) {
|
|
70
|
-
// Add any text between matches as a paragraph
|
|
71
|
-
if (match.index > lastIndex) {
|
|
72
|
-
const text = normalizedHtml.substring(lastIndex, match.index).trim();
|
|
73
|
-
if (text && !text.match(/^\s*$/)) {
|
|
74
|
-
sections.push({ type: 'paragraph', content: this.stripTags(text) });
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const fullMatch = match[0];
|
|
79
|
-
|
|
80
|
-
// Determine block type and extract content
|
|
81
|
-
if (fullMatch.match(/<h([1-6])/i)) {
|
|
82
|
-
const level = fullMatch.match(/<h([1-6])/i)[1];
|
|
83
|
-
const content = fullMatch.replace(/<\/?h[1-6](?:\s[^>]*)?>/gi, '');
|
|
84
|
-
sections.push({ type: 'heading', level: parseInt(level), content: this.stripTags(content) });
|
|
85
|
-
}
|
|
86
|
-
else if (fullMatch.match(/<p(?:\s|>)/i)) {
|
|
87
|
-
const content = fullMatch.replace(/<\/?p(?:\s[^>]*)?>/gi, '');
|
|
88
|
-
sections.push({ type: 'paragraph', content: this.stripTags(content) });
|
|
89
|
-
}
|
|
90
|
-
else if (fullMatch.match(/<(ul|ol)/i)) {
|
|
91
|
-
const listType = fullMatch.match(/<(ul|ol)/i)[1];
|
|
92
|
-
const items = this.parseListItems(fullMatch);
|
|
93
|
-
sections.push({ type: 'list', ordered: listType === 'ol', items });
|
|
94
|
-
}
|
|
95
|
-
else if (fullMatch.match(/<blockquote/i)) {
|
|
96
|
-
const content = fullMatch.replace(/<\/?blockquote(?:\s[^>]*)?>/gi, '');
|
|
97
|
-
sections.push({ type: 'quote', content: this.stripTags(content) });
|
|
98
|
-
}
|
|
99
|
-
else if (fullMatch.match(/<pre/i)) {
|
|
100
|
-
const content = fullMatch.replace(/<\/?(?:pre|code)(?:\s[^>]*)?>/gi, '');
|
|
101
|
-
sections.push({ type: 'code', content: this.decodeHtml(content) });
|
|
102
|
-
}
|
|
103
|
-
else if (fullMatch.match(/<img/i)) {
|
|
104
|
-
const attrs = this.parseImageAttributes(fullMatch);
|
|
105
|
-
sections.push({ type: 'image', ...attrs });
|
|
106
|
-
}
|
|
107
|
-
else if (fullMatch.match(/<hr/i)) {
|
|
108
|
-
sections.push({ type: 'separator' });
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
lastIndex = match.index + fullMatch.length;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Add any remaining content
|
|
115
|
-
if (lastIndex < normalizedHtml.length) {
|
|
116
|
-
const text = normalizedHtml.substring(lastIndex).trim();
|
|
117
|
-
if (text && !text.match(/^\s*$/)) {
|
|
118
|
-
sections.push({ type: 'paragraph', content: this.stripTags(text) });
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return sections;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Parse list items from HTML list
|
|
127
|
-
*/
|
|
128
|
-
static parseListItems(listHtml) {
|
|
129
|
-
const items = [];
|
|
130
|
-
const itemPattern = /<li(?:\s[^>]*)?>(.+?)<\/li>/gis;
|
|
131
|
-
let match;
|
|
132
|
-
|
|
133
|
-
while ((match = itemPattern.exec(listHtml)) !== null) {
|
|
134
|
-
items.push(this.stripTags(match[1].trim()));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return items;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Parse image attributes from img tag
|
|
142
|
-
*/
|
|
143
|
-
static parseImageAttributes(imgTag) {
|
|
144
|
-
const attrs = {};
|
|
145
|
-
|
|
146
|
-
// Extract src
|
|
147
|
-
const srcMatch = imgTag.match(/src=["']([^"']+)["']/i);
|
|
148
|
-
if (srcMatch) attrs.url = srcMatch[1];
|
|
149
|
-
|
|
150
|
-
// Extract alt text
|
|
151
|
-
const altMatch = imgTag.match(/alt=["']([^"']+)["']/i);
|
|
152
|
-
if (altMatch) attrs.alt = altMatch[1];
|
|
153
|
-
|
|
154
|
-
// Extract title
|
|
155
|
-
const titleMatch = imgTag.match(/title=["']([^"']+)["']/i);
|
|
156
|
-
if (titleMatch) attrs.caption = titleMatch[1];
|
|
157
|
-
|
|
158
|
-
return attrs;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Create a Gutenberg block from a section
|
|
163
|
-
*/
|
|
164
|
-
static createBlock(section) {
|
|
165
|
-
switch (section.type) {
|
|
166
|
-
case 'heading':
|
|
167
|
-
return this.createHeadingBlock(section.level, section.content);
|
|
168
|
-
|
|
169
|
-
case 'paragraph':
|
|
170
|
-
return this.createParagraphBlock(section.content);
|
|
171
|
-
|
|
172
|
-
case 'list':
|
|
173
|
-
return this.createListBlock(section.items, section.ordered);
|
|
174
|
-
|
|
175
|
-
case 'quote':
|
|
176
|
-
return this.createQuoteBlock(section.content);
|
|
177
|
-
|
|
178
|
-
case 'code':
|
|
179
|
-
return this.createCodeBlock(section.content);
|
|
180
|
-
|
|
181
|
-
case 'image':
|
|
182
|
-
return this.createImageBlock(section.url, section.alt, section.caption);
|
|
183
|
-
|
|
184
|
-
case 'separator':
|
|
185
|
-
return this.createSeparatorBlock();
|
|
186
|
-
|
|
187
|
-
default:
|
|
188
|
-
return this.createParagraphBlock(section.content || '');
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Create heading block
|
|
194
|
-
*/
|
|
195
|
-
static createHeadingBlock(level, content) {
|
|
196
|
-
return `<!-- wp:heading {"level":${level}} -->
|
|
197
|
-
<h${level} class="wp-block-heading">${this.escapeHtml(content)}</h${level}>
|
|
198
|
-
<!-- /wp:heading -->`;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Create paragraph block
|
|
203
|
-
*/
|
|
204
|
-
static createParagraphBlock(content) {
|
|
205
|
-
// Handle empty paragraphs
|
|
206
|
-
if (!content || content.trim() === '') {
|
|
207
|
-
return '';
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return `<!-- wp:paragraph -->
|
|
211
|
-
<p>${this.escapeHtml(content)}</p>
|
|
212
|
-
<!-- /wp:paragraph -->`;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Create list block
|
|
217
|
-
*/
|
|
218
|
-
static createListBlock(items, ordered = false) {
|
|
219
|
-
const tag = ordered ? 'ol' : 'ul';
|
|
220
|
-
const blockName = ordered ? 'list' : 'list';
|
|
221
|
-
const listItems = items.map(item => `<li>${this.escapeHtml(item)}</li>`).join('\n');
|
|
222
|
-
|
|
223
|
-
const attributes = ordered ? ' {"ordered":true}' : '';
|
|
224
|
-
|
|
225
|
-
return `<!-- wp:list${attributes} -->
|
|
226
|
-
<${tag} class="wp-block-list">${listItems}</${tag}>
|
|
227
|
-
<!-- /wp:list -->`;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Create quote block
|
|
232
|
-
*/
|
|
233
|
-
static createQuoteBlock(content) {
|
|
234
|
-
return `<!-- wp:quote -->
|
|
235
|
-
<blockquote class="wp-block-quote">
|
|
236
|
-
<p>${this.escapeHtml(content)}</p>
|
|
237
|
-
</blockquote>
|
|
238
|
-
<!-- /wp:quote -->`;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Create code block
|
|
243
|
-
*/
|
|
244
|
-
static createCodeBlock(code) {
|
|
245
|
-
// Escape the code content for HTML
|
|
246
|
-
const escapedCode = this.escapeHtml(code);
|
|
247
|
-
|
|
248
|
-
return `<!-- wp:code -->
|
|
249
|
-
<pre class="wp-block-code"><code>${escapedCode}</code></pre>
|
|
250
|
-
<!-- /wp:code -->`;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Create image block
|
|
255
|
-
*/
|
|
256
|
-
static createImageBlock(url, alt = '', caption = '') {
|
|
257
|
-
let attributes = {};
|
|
258
|
-
if (alt) attributes.alt = alt;
|
|
259
|
-
|
|
260
|
-
const attributesJson = Object.keys(attributes).length > 0
|
|
261
|
-
? ' ' + JSON.stringify(attributes)
|
|
262
|
-
: '';
|
|
263
|
-
|
|
264
|
-
let imageHtml = `<!-- wp:image${attributesJson} -->
|
|
265
|
-
<figure class="wp-block-image"><img src="${url}"${alt ? ` alt="${this.escapeHtml(alt)}"` : ''}/>`;
|
|
266
|
-
|
|
267
|
-
if (caption) {
|
|
268
|
-
imageHtml += `<figcaption class="wp-element-caption">${this.escapeHtml(caption)}</figcaption>`;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
imageHtml += `</figure>
|
|
272
|
-
<!-- /wp:image -->`;
|
|
273
|
-
|
|
274
|
-
return imageHtml;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Create separator block
|
|
279
|
-
*/
|
|
280
|
-
static createSeparatorBlock() {
|
|
281
|
-
return `<!-- wp:separator -->
|
|
282
|
-
<hr class="wp-block-separator has-alpha-channel-opacity"/>
|
|
283
|
-
<!-- /wp:separator -->`;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Create columns block for advanced layouts
|
|
288
|
-
*/
|
|
289
|
-
static createColumnsBlock(columns) {
|
|
290
|
-
const columnCount = columns.length;
|
|
291
|
-
let columnsHtml = `<!-- wp:columns {"columns":${columnCount}} -->\n<div class="wp-block-columns">`;
|
|
292
|
-
|
|
293
|
-
columns.forEach(column => {
|
|
294
|
-
columnsHtml += `\n<!-- wp:column -->\n<div class="wp-block-column">`;
|
|
295
|
-
columnsHtml += `\n${column}`;
|
|
296
|
-
columnsHtml += `\n</div>\n<!-- /wp:column -->`;
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
columnsHtml += `\n</div>\n<!-- /wp:columns -->`;
|
|
300
|
-
return columnsHtml;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Create button block
|
|
305
|
-
*/
|
|
306
|
-
static createButtonBlock(text, url = '#', align = 'none') {
|
|
307
|
-
return `<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"${align}"}} -->
|
|
308
|
-
<div class="wp-block-buttons">
|
|
309
|
-
<!-- wp:button -->
|
|
310
|
-
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="${url}">${this.escapeHtml(text)}</a></div>
|
|
311
|
-
<!-- /wp:button -->
|
|
312
|
-
</div>
|
|
313
|
-
<!-- /wp:buttons -->`;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Create table block
|
|
318
|
-
*/
|
|
319
|
-
static createTableBlock(headers, rows) {
|
|
320
|
-
let tableHtml = `<!-- wp:table -->
|
|
321
|
-
<figure class="wp-block-table"><table class="wp-block-table">`;
|
|
322
|
-
|
|
323
|
-
// Add headers
|
|
324
|
-
if (headers && headers.length > 0) {
|
|
325
|
-
tableHtml += '\n<thead>\n<tr>';
|
|
326
|
-
headers.forEach(header => {
|
|
327
|
-
tableHtml += `<th>${this.escapeHtml(header)}</th>`;
|
|
328
|
-
});
|
|
329
|
-
tableHtml += '</tr>\n</thead>';
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Add rows
|
|
333
|
-
tableHtml += '\n<tbody>';
|
|
334
|
-
rows.forEach(row => {
|
|
335
|
-
tableHtml += '\n<tr>';
|
|
336
|
-
row.forEach(cell => {
|
|
337
|
-
tableHtml += `<td>${this.escapeHtml(cell)}</td>`;
|
|
338
|
-
});
|
|
339
|
-
tableHtml += '</tr>';
|
|
340
|
-
});
|
|
341
|
-
tableHtml += '\n</tbody>';
|
|
342
|
-
|
|
343
|
-
tableHtml += `\n</table></figure>
|
|
344
|
-
<!-- /wp:table -->`;
|
|
345
|
-
|
|
346
|
-
return tableHtml;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Utility: Strip HTML tags from text
|
|
351
|
-
*/
|
|
352
|
-
static stripTags(text) {
|
|
353
|
-
return text.replace(/<[^>]+>/g, '').trim();
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Utility: Escape HTML special characters
|
|
358
|
-
*/
|
|
359
|
-
static escapeHtml(text) {
|
|
360
|
-
const map = {
|
|
361
|
-
'&': '&',
|
|
362
|
-
'<': '<',
|
|
363
|
-
'>': '>',
|
|
364
|
-
'"': '"',
|
|
365
|
-
"'": ''',
|
|
366
|
-
"/": '/'
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
return text.replace(/[&<>"'/]/g, char => map[char]);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Utility: Decode HTML entities
|
|
374
|
-
*/
|
|
375
|
-
static decodeHtml(text) {
|
|
376
|
-
const entities = {
|
|
377
|
-
'&': '&',
|
|
378
|
-
'<': '<',
|
|
379
|
-
'>': '>',
|
|
380
|
-
'"': '"',
|
|
381
|
-
''': "'",
|
|
382
|
-
'/': '/',
|
|
383
|
-
''': "'",
|
|
384
|
-
' ': ' '
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
return text.replace(/&[#a-z0-9]+;/gi, entity => entities[entity] || entity);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Convert markdown to Gutenberg blocks (bonus feature)
|
|
392
|
-
*/
|
|
393
|
-
static markdownToGutenberg(markdown) {
|
|
394
|
-
// First convert markdown to HTML (simplified version)
|
|
395
|
-
let html = markdown
|
|
396
|
-
// Headers
|
|
397
|
-
.replace(/^### (.+)$/gm, '<h3>$1</h3>')
|
|
398
|
-
.replace(/^## (.+)$/gm, '<h2>$1</h2>')
|
|
399
|
-
.replace(/^# (.+)$/gm, '<h1>$1</h1>')
|
|
400
|
-
// Bold
|
|
401
|
-
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
402
|
-
// Italic
|
|
403
|
-
.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
|
404
|
-
// Code blocks
|
|
405
|
-
.replace(/```[\s\S]*?```/g, match => {
|
|
406
|
-
const code = match.slice(3, -3).trim();
|
|
407
|
-
return `<pre><code>${code}</code></pre>`;
|
|
408
|
-
})
|
|
409
|
-
// Inline code
|
|
410
|
-
.replace(/`(.+?)`/g, '<code>$1</code>')
|
|
411
|
-
// Blockquotes
|
|
412
|
-
.replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>')
|
|
413
|
-
// Horizontal rules
|
|
414
|
-
.replace(/^---$/gm, '<hr>')
|
|
415
|
-
// Lists (simplified)
|
|
416
|
-
.replace(/^- (.+)$/gm, '<li>$1</li>')
|
|
417
|
-
.replace(/^(\d+)\. (.+)$/gm, '<li>$2</li>');
|
|
418
|
-
|
|
419
|
-
// Wrap consecutive li elements in ul/ol
|
|
420
|
-
html = html.replace(/(<li>.*<\/li>\n?)+/g, match => {
|
|
421
|
-
return `<ul>${match}</ul>`;
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
// Convert paragraphs
|
|
425
|
-
const lines = html.split('\n');
|
|
426
|
-
const processedLines = [];
|
|
427
|
-
let inBlock = false;
|
|
428
|
-
|
|
429
|
-
lines.forEach(line => {
|
|
430
|
-
const trimmed = line.trim();
|
|
431
|
-
if (trimmed === '') {
|
|
432
|
-
inBlock = false;
|
|
433
|
-
} else if (!trimmed.startsWith('<')) {
|
|
434
|
-
processedLines.push(`<p>${trimmed}</p>`);
|
|
435
|
-
} else {
|
|
436
|
-
processedLines.push(trimmed);
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
html = processedLines.join('\n');
|
|
441
|
-
|
|
442
|
-
// Now convert HTML to Gutenberg
|
|
443
|
-
return this.htmlToGutenberg(html);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
export default GutenbergConverter;
|