@marvalt/wparser 0.1.59 → 0.1.61
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/dist/index.cjs +140 -210
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +140 -210
- package/dist/index.esm.js.map +1 -1
- package/dist/registry/defaultRegistry.d.ts.map +1 -1
- package/dist/utils/blockExtractors.d.ts +1 -1
- package/dist/utils/blockExtractors.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1406,115 +1406,6 @@ function renderBlock(block, registry, key, options, page) {
|
|
|
1406
1406
|
return jsxRuntimeExports.jsx(React.Fragment, { children: node }, key);
|
|
1407
1407
|
}
|
|
1408
1408
|
|
|
1409
|
-
/**
|
|
1410
|
-
* Parse shortcode attributes from a string
|
|
1411
|
-
* Supports: [shortcode attr="value" attr2="value2"]
|
|
1412
|
-
*/
|
|
1413
|
-
function parseShortcodeAttrs(attrString) {
|
|
1414
|
-
if (!attrString || !attrString.trim()) {
|
|
1415
|
-
return {};
|
|
1416
|
-
}
|
|
1417
|
-
const attrs = {};
|
|
1418
|
-
// Match key="value" or key='value' patterns
|
|
1419
|
-
const attrRegex = /(\w+)=["']([^"']+)["']/g;
|
|
1420
|
-
let match;
|
|
1421
|
-
while ((match = attrRegex.exec(attrString)) !== null) {
|
|
1422
|
-
attrs[match[1]] = match[2];
|
|
1423
|
-
}
|
|
1424
|
-
return attrs;
|
|
1425
|
-
}
|
|
1426
|
-
/**
|
|
1427
|
-
* Find all shortcodes in text
|
|
1428
|
-
* Supports: [shortcode], [shortcode attr="value"], [shortcode]content[/shortcode]
|
|
1429
|
-
*/
|
|
1430
|
-
function findShortcodes(text) {
|
|
1431
|
-
const shortcodes = [];
|
|
1432
|
-
// First, find closing tag pairs: [shortcode]content[/shortcode]
|
|
1433
|
-
const closingTagRegex = /\[(\w+)(?:\s+([^\]]+))?\](.*?)\[\/\1\]/gs;
|
|
1434
|
-
let match;
|
|
1435
|
-
const processedRanges = [];
|
|
1436
|
-
while ((match = closingTagRegex.exec(text)) !== null) {
|
|
1437
|
-
const name = match[1];
|
|
1438
|
-
const attrString = match[2] || '';
|
|
1439
|
-
const content = match[3] || '';
|
|
1440
|
-
const attrs = parseShortcodeAttrs(attrString);
|
|
1441
|
-
shortcodes.push({
|
|
1442
|
-
name,
|
|
1443
|
-
attrs,
|
|
1444
|
-
content,
|
|
1445
|
-
fullMatch: match[0],
|
|
1446
|
-
startIndex: match.index,
|
|
1447
|
-
endIndex: match.index + match[0].length,
|
|
1448
|
-
});
|
|
1449
|
-
processedRanges.push({
|
|
1450
|
-
start: match.index,
|
|
1451
|
-
end: match.index + match[0].length,
|
|
1452
|
-
});
|
|
1453
|
-
}
|
|
1454
|
-
// Then, find self-closing: [shortcode attr="value"]
|
|
1455
|
-
// Skip ranges already processed by closing tags
|
|
1456
|
-
const selfClosingRegex = /\[(\w+)(?:\s+([^\]]+))?\]/g;
|
|
1457
|
-
let selfClosingMatch;
|
|
1458
|
-
while ((selfClosingMatch = selfClosingRegex.exec(text)) !== null) {
|
|
1459
|
-
// Check if this match is within a processed range
|
|
1460
|
-
const isProcessed = processedRanges.some(range => selfClosingMatch.index >= range.start && selfClosingMatch.index < range.end);
|
|
1461
|
-
if (isProcessed) {
|
|
1462
|
-
continue;
|
|
1463
|
-
}
|
|
1464
|
-
const name = selfClosingMatch[1];
|
|
1465
|
-
const attrString = selfClosingMatch[2] || '';
|
|
1466
|
-
const attrs = parseShortcodeAttrs(attrString);
|
|
1467
|
-
shortcodes.push({
|
|
1468
|
-
name,
|
|
1469
|
-
attrs,
|
|
1470
|
-
fullMatch: selfClosingMatch[0],
|
|
1471
|
-
startIndex: selfClosingMatch.index,
|
|
1472
|
-
endIndex: selfClosingMatch.index + selfClosingMatch[0].length,
|
|
1473
|
-
});
|
|
1474
|
-
}
|
|
1475
|
-
// Sort by start index
|
|
1476
|
-
shortcodes.sort((a, b) => a.startIndex - b.startIndex);
|
|
1477
|
-
return shortcodes;
|
|
1478
|
-
}
|
|
1479
|
-
/**
|
|
1480
|
-
* Render text with shortcodes replaced by React components
|
|
1481
|
-
*/
|
|
1482
|
-
function renderTextWithShortcodes(text, registry) {
|
|
1483
|
-
const shortcodes = findShortcodes(text);
|
|
1484
|
-
if (shortcodes.length === 0) {
|
|
1485
|
-
return [text];
|
|
1486
|
-
}
|
|
1487
|
-
const parts = [];
|
|
1488
|
-
let lastIndex = 0;
|
|
1489
|
-
for (const shortcode of shortcodes) {
|
|
1490
|
-
// Add text before shortcode
|
|
1491
|
-
if (shortcode.startIndex > lastIndex) {
|
|
1492
|
-
const textBefore = text.substring(lastIndex, shortcode.startIndex);
|
|
1493
|
-
if (textBefore) {
|
|
1494
|
-
parts.push(textBefore);
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
// Render shortcode
|
|
1498
|
-
const renderer = registry.shortcodes[shortcode.name];
|
|
1499
|
-
if (renderer) {
|
|
1500
|
-
parts.push(jsxRuntimeExports.jsx(React.Fragment, { children: renderer(shortcode.attrs, shortcode.content) }, `shortcode-${shortcode.startIndex}`));
|
|
1501
|
-
}
|
|
1502
|
-
else {
|
|
1503
|
-
// Keep original shortcode if no renderer found
|
|
1504
|
-
parts.push(shortcode.fullMatch);
|
|
1505
|
-
}
|
|
1506
|
-
lastIndex = shortcode.endIndex;
|
|
1507
|
-
}
|
|
1508
|
-
// Add remaining text
|
|
1509
|
-
if (lastIndex < text.length) {
|
|
1510
|
-
const remainingText = text.substring(lastIndex);
|
|
1511
|
-
if (remainingText) {
|
|
1512
|
-
parts.push(remainingText);
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
return parts;
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
1409
|
/**
|
|
1519
1410
|
* Content extraction utilities for WordPress blocks
|
|
1520
1411
|
* Extracts text content from various block formats
|
|
@@ -2258,6 +2149,10 @@ function extractSpacerHeight(block) {
|
|
|
2258
2149
|
return height;
|
|
2259
2150
|
}
|
|
2260
2151
|
if (typeof height === 'string') {
|
|
2152
|
+
// If it's a CSS var or clamp, return as-is
|
|
2153
|
+
if (height.startsWith('var(') || height.includes('clamp(')) {
|
|
2154
|
+
return height;
|
|
2155
|
+
}
|
|
2261
2156
|
// Parse "100px" or "100" to number
|
|
2262
2157
|
const match = height.match(/^(\d+)/);
|
|
2263
2158
|
if (match) {
|
|
@@ -2268,9 +2163,17 @@ function extractSpacerHeight(block) {
|
|
|
2268
2163
|
if (block.innerHTML) {
|
|
2269
2164
|
const styleMatch = block.innerHTML.match(/style=["']([^"']+)["']/i);
|
|
2270
2165
|
if (styleMatch) {
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2166
|
+
// First look for CSS var or clamp
|
|
2167
|
+
const cssVarMatch = styleMatch[1].match(/height:\s*([^;]+);?/i);
|
|
2168
|
+
if (cssVarMatch) {
|
|
2169
|
+
const cssHeight = cssVarMatch[1].trim();
|
|
2170
|
+
if (cssHeight.startsWith('var(') || cssHeight.includes('clamp(')) {
|
|
2171
|
+
return cssHeight;
|
|
2172
|
+
}
|
|
2173
|
+
const numericMatch = cssHeight.match(/^(\d+)/);
|
|
2174
|
+
if (numericMatch) {
|
|
2175
|
+
return parseInt(numericMatch[1], 10);
|
|
2176
|
+
}
|
|
2274
2177
|
}
|
|
2275
2178
|
}
|
|
2276
2179
|
}
|
|
@@ -2374,109 +2277,24 @@ function buildClassName(...classes) {
|
|
|
2374
2277
|
function getSpacing(spacingConfig, key, fallback) {
|
|
2375
2278
|
return spacingConfig?.[key] || fallback;
|
|
2376
2279
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
return headingConfig?.h1 || 'mt-10 mb-8';
|
|
2382
|
-
if (level === 2)
|
|
2383
|
-
return headingConfig?.h2 || 'mt-8 mb-6';
|
|
2384
|
-
return headingConfig?.h3 || 'mt-6 mb-4';
|
|
2385
|
-
}
|
|
2386
|
-
const Paragraph = ({ block, context }) => {
|
|
2387
|
-
const attrs = block.attributes || {};
|
|
2388
|
-
const textAlign = getTextAlignClasses(attrs['align']);
|
|
2389
|
-
const spacing = getSpacing(context.spacingConfig || context.registry.spacingConfig, 'paragraph', 'my-6');
|
|
2390
|
-
// Extract text color if specified, otherwise inherit from parent (CSS variables handle default)
|
|
2391
|
-
const textColor = extractTextColor(block, context);
|
|
2392
|
-
const textColorClass = textColor || ''; // Don't hardcode - let CSS variables handle default
|
|
2393
|
-
// Extract font size if specified
|
|
2394
|
-
const fontSize = extractFontSize(block, context);
|
|
2395
|
-
const fontSizeClass = fontSize ? getFontSizeClasses(fontSize) : '';
|
|
2396
|
-
// Check if innerHTML contains HTML elements (like links, strong, em, etc.)
|
|
2397
|
-
const hasHTML = block.innerHTML && /<[a-z][\s\S]*>/i.test(block.innerHTML);
|
|
2398
|
-
// Check if content contains shortcodes (check both HTML and text content)
|
|
2399
|
-
const htmlContent = block.innerHTML || '';
|
|
2400
|
-
const textContent = getBlockTextContent(block);
|
|
2401
|
-
const hasShortcodes = /\[(\w+)/.test(htmlContent) || /\[(\w+)/.test(textContent);
|
|
2402
|
-
// If shortcodes are present, always process them (even if HTML is also present)
|
|
2403
|
-
if (hasShortcodes && context.registry.shortcodes) {
|
|
2404
|
-
// Process shortcodes from text content
|
|
2405
|
-
const parts = renderTextWithShortcodes(textContent, context.registry);
|
|
2406
|
-
// Check if any part is a block-level element (section, div, etc.)
|
|
2407
|
-
const isBlockLevelElement = (element) => {
|
|
2408
|
-
if (!React.isValidElement(element)) {
|
|
2409
|
-
return false;
|
|
2410
|
-
}
|
|
2411
|
-
const type = element.type;
|
|
2412
|
-
// Check for block-level HTML elements
|
|
2413
|
-
if (typeof type === 'string' && ['section', 'div', 'article', 'header', 'footer', 'aside', 'nav'].includes(type)) {
|
|
2414
|
-
return true;
|
|
2415
|
-
}
|
|
2416
|
-
// Check if it's React.Fragment - recursively check its children
|
|
2417
|
-
if (type === React.Fragment) {
|
|
2418
|
-
const fragmentProps = element.props;
|
|
2419
|
-
const children = React.Children.toArray(fragmentProps.children);
|
|
2420
|
-
return children.some((child) => isBlockLevelElement(child));
|
|
2421
|
-
}
|
|
2422
|
-
// Check if it's a React component (likely block-level)
|
|
2423
|
-
if (typeof type === 'function' || (typeof type === 'object' && type !== null && type !== React.Fragment)) {
|
|
2424
|
-
return true;
|
|
2425
|
-
}
|
|
2426
|
-
return false;
|
|
2427
|
-
};
|
|
2428
|
-
const hasBlockLevelContent = React.Children.toArray(parts).some((part) => isBlockLevelElement(part));
|
|
2429
|
-
if (hasBlockLevelContent) {
|
|
2430
|
-
// Render block-level content without <p> wrapper, but add spacing wrapper
|
|
2431
|
-
const fontSize = extractFontSize(block, context);
|
|
2432
|
-
const fontSizeClass = fontSize ? getFontSizeClasses(fontSize) : '';
|
|
2433
|
-
return jsxRuntimeExports.jsx("div", { className: buildClassName(spacing, textColorClass, fontSizeClass), children: parts });
|
|
2434
|
-
}
|
|
2435
|
-
// Render shortcode parts inside paragraph (shortcodes are processed as React components)
|
|
2436
|
-
return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), children: parts });
|
|
2437
|
-
}
|
|
2438
|
-
// If innerHTML contains HTML elements but no shortcodes, render it directly (preserves links, formatting, etc.)
|
|
2439
|
-
if (hasHTML && block.innerHTML) {
|
|
2440
|
-
// Check if innerHTML already contains a paragraph tag - if so, extract content to avoid double nesting
|
|
2441
|
-
const trimmedContent = htmlContent.trim();
|
|
2442
|
-
if (trimmedContent.startsWith('<p') && trimmedContent.endsWith('</p>')) {
|
|
2443
|
-
// Extract content from paragraph tag
|
|
2444
|
-
const contentMatch = trimmedContent.match(/<p[^>]*>(.*?)<\/p>/s);
|
|
2445
|
-
if (contentMatch) {
|
|
2446
|
-
const innerContent = contentMatch[1];
|
|
2447
|
-
// Check if inner content also has a paragraph (double nesting)
|
|
2448
|
-
if (innerContent.trim().startsWith('<p')) {
|
|
2449
|
-
// Render as div to avoid invalid HTML
|
|
2450
|
-
return jsxRuntimeExports.jsx("div", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), dangerouslySetInnerHTML: { __html: innerContent } });
|
|
2451
|
-
}
|
|
2452
|
-
// Render the extracted content in a paragraph
|
|
2453
|
-
return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), dangerouslySetInnerHTML: { __html: innerContent } });
|
|
2454
|
-
}
|
|
2455
|
-
}
|
|
2456
|
-
return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), dangerouslySetInnerHTML: { __html: htmlContent } });
|
|
2280
|
+
const Paragraph = ({ block }) => {
|
|
2281
|
+
// If innerHTML exists, render it as-is to preserve WP classes/styles
|
|
2282
|
+
if (block.innerHTML) {
|
|
2283
|
+
return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: block.innerHTML } });
|
|
2457
2284
|
}
|
|
2458
|
-
|
|
2459
|
-
return jsxRuntimeExports.jsx("p", {
|
|
2285
|
+
const textContent = getBlockTextContent(block);
|
|
2286
|
+
return jsxRuntimeExports.jsx("p", { children: textContent });
|
|
2460
2287
|
};
|
|
2461
|
-
const Heading = ({ block, children
|
|
2288
|
+
const Heading = ({ block, children }) => {
|
|
2289
|
+
// If innerHTML exists, render it as-is to preserve WP classes/styles (font sizes, weights)
|
|
2290
|
+
if (block.innerHTML) {
|
|
2291
|
+
return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: block.innerHTML } });
|
|
2292
|
+
}
|
|
2462
2293
|
const attrs = block.attributes || {};
|
|
2463
2294
|
const { level = 2 } = attrs;
|
|
2464
2295
|
const content = getBlockTextContent(block);
|
|
2465
|
-
const textAlign = getTextAlignClasses(attrs['textAlign']);
|
|
2466
|
-
// Extract font size - check both fontSize attribute and style.typography.fontSize
|
|
2467
|
-
const extractedFontSize = extractFontSize(block, context);
|
|
2468
|
-
const fontSize = extractedFontSize ? getFontSizeClasses(extractedFontSize) : '';
|
|
2469
2296
|
const Tag = `h${Math.min(Math.max(Number(level) || 2, 1), 6)}`;
|
|
2470
|
-
|
|
2471
|
-
// Only use Tailwind class if explicitly set via fontSize attribute
|
|
2472
|
-
const sizeClass = fontSize || ''; // Let CSS variables handle default sizes
|
|
2473
|
-
// Get spacing from config with improved defaults
|
|
2474
|
-
const spacingClass = getHeadingSpacing(context.spacingConfig || context.registry.spacingConfig, level);
|
|
2475
|
-
// Extract text color if specified, otherwise inherit from parent (CSS variables handle default)
|
|
2476
|
-
const textColor = extractTextColor(block, context);
|
|
2477
|
-
const textColorClass = textColor || ''; // Don't hardcode - let CSS variables handle default
|
|
2478
|
-
// Don't hardcode font-bold - let CSS variables handle font weight from WordPress
|
|
2479
|
-
return (jsxRuntimeExports.jsx(Tag, { className: buildClassName(textColorClass, sizeClass, textAlign, spacingClass), children: children ?? content }));
|
|
2297
|
+
return jsxRuntimeExports.jsx(Tag, { children: children ?? content });
|
|
2480
2298
|
};
|
|
2481
2299
|
const Image = ({ block, context }) => {
|
|
2482
2300
|
const imageAttrs = getImageAttributes(block);
|
|
@@ -2850,7 +2668,10 @@ function createDefaultRegistry(colorMapper, spacingConfig) {
|
|
|
2850
2668
|
// Spacer block - adds vertical spacing
|
|
2851
2669
|
'core/spacer': ({ block }) => {
|
|
2852
2670
|
const height = extractSpacerHeight(block);
|
|
2853
|
-
if (
|
|
2671
|
+
if (typeof height === 'string') {
|
|
2672
|
+
return jsxRuntimeExports.jsx("div", { style: { height }, "aria-hidden": "true" });
|
|
2673
|
+
}
|
|
2674
|
+
if (typeof height === 'number' && height > 0) {
|
|
2854
2675
|
return jsxRuntimeExports.jsx("div", { style: { height: `${height}px` }, "aria-hidden": "true" });
|
|
2855
2676
|
}
|
|
2856
2677
|
// Default fallback if height not found
|
|
@@ -3139,6 +2960,115 @@ class WPErrorBoundary extends React.Component {
|
|
|
3139
2960
|
}
|
|
3140
2961
|
}
|
|
3141
2962
|
|
|
2963
|
+
/**
|
|
2964
|
+
* Parse shortcode attributes from a string
|
|
2965
|
+
* Supports: [shortcode attr="value" attr2="value2"]
|
|
2966
|
+
*/
|
|
2967
|
+
function parseShortcodeAttrs(attrString) {
|
|
2968
|
+
if (!attrString || !attrString.trim()) {
|
|
2969
|
+
return {};
|
|
2970
|
+
}
|
|
2971
|
+
const attrs = {};
|
|
2972
|
+
// Match key="value" or key='value' patterns
|
|
2973
|
+
const attrRegex = /(\w+)=["']([^"']+)["']/g;
|
|
2974
|
+
let match;
|
|
2975
|
+
while ((match = attrRegex.exec(attrString)) !== null) {
|
|
2976
|
+
attrs[match[1]] = match[2];
|
|
2977
|
+
}
|
|
2978
|
+
return attrs;
|
|
2979
|
+
}
|
|
2980
|
+
/**
|
|
2981
|
+
* Find all shortcodes in text
|
|
2982
|
+
* Supports: [shortcode], [shortcode attr="value"], [shortcode]content[/shortcode]
|
|
2983
|
+
*/
|
|
2984
|
+
function findShortcodes(text) {
|
|
2985
|
+
const shortcodes = [];
|
|
2986
|
+
// First, find closing tag pairs: [shortcode]content[/shortcode]
|
|
2987
|
+
const closingTagRegex = /\[(\w+)(?:\s+([^\]]+))?\](.*?)\[\/\1\]/gs;
|
|
2988
|
+
let match;
|
|
2989
|
+
const processedRanges = [];
|
|
2990
|
+
while ((match = closingTagRegex.exec(text)) !== null) {
|
|
2991
|
+
const name = match[1];
|
|
2992
|
+
const attrString = match[2] || '';
|
|
2993
|
+
const content = match[3] || '';
|
|
2994
|
+
const attrs = parseShortcodeAttrs(attrString);
|
|
2995
|
+
shortcodes.push({
|
|
2996
|
+
name,
|
|
2997
|
+
attrs,
|
|
2998
|
+
content,
|
|
2999
|
+
fullMatch: match[0],
|
|
3000
|
+
startIndex: match.index,
|
|
3001
|
+
endIndex: match.index + match[0].length,
|
|
3002
|
+
});
|
|
3003
|
+
processedRanges.push({
|
|
3004
|
+
start: match.index,
|
|
3005
|
+
end: match.index + match[0].length,
|
|
3006
|
+
});
|
|
3007
|
+
}
|
|
3008
|
+
// Then, find self-closing: [shortcode attr="value"]
|
|
3009
|
+
// Skip ranges already processed by closing tags
|
|
3010
|
+
const selfClosingRegex = /\[(\w+)(?:\s+([^\]]+))?\]/g;
|
|
3011
|
+
let selfClosingMatch;
|
|
3012
|
+
while ((selfClosingMatch = selfClosingRegex.exec(text)) !== null) {
|
|
3013
|
+
// Check if this match is within a processed range
|
|
3014
|
+
const isProcessed = processedRanges.some(range => selfClosingMatch.index >= range.start && selfClosingMatch.index < range.end);
|
|
3015
|
+
if (isProcessed) {
|
|
3016
|
+
continue;
|
|
3017
|
+
}
|
|
3018
|
+
const name = selfClosingMatch[1];
|
|
3019
|
+
const attrString = selfClosingMatch[2] || '';
|
|
3020
|
+
const attrs = parseShortcodeAttrs(attrString);
|
|
3021
|
+
shortcodes.push({
|
|
3022
|
+
name,
|
|
3023
|
+
attrs,
|
|
3024
|
+
fullMatch: selfClosingMatch[0],
|
|
3025
|
+
startIndex: selfClosingMatch.index,
|
|
3026
|
+
endIndex: selfClosingMatch.index + selfClosingMatch[0].length,
|
|
3027
|
+
});
|
|
3028
|
+
}
|
|
3029
|
+
// Sort by start index
|
|
3030
|
+
shortcodes.sort((a, b) => a.startIndex - b.startIndex);
|
|
3031
|
+
return shortcodes;
|
|
3032
|
+
}
|
|
3033
|
+
/**
|
|
3034
|
+
* Render text with shortcodes replaced by React components
|
|
3035
|
+
*/
|
|
3036
|
+
function renderTextWithShortcodes(text, registry) {
|
|
3037
|
+
const shortcodes = findShortcodes(text);
|
|
3038
|
+
if (shortcodes.length === 0) {
|
|
3039
|
+
return [text];
|
|
3040
|
+
}
|
|
3041
|
+
const parts = [];
|
|
3042
|
+
let lastIndex = 0;
|
|
3043
|
+
for (const shortcode of shortcodes) {
|
|
3044
|
+
// Add text before shortcode
|
|
3045
|
+
if (shortcode.startIndex > lastIndex) {
|
|
3046
|
+
const textBefore = text.substring(lastIndex, shortcode.startIndex);
|
|
3047
|
+
if (textBefore) {
|
|
3048
|
+
parts.push(textBefore);
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
// Render shortcode
|
|
3052
|
+
const renderer = registry.shortcodes[shortcode.name];
|
|
3053
|
+
if (renderer) {
|
|
3054
|
+
parts.push(jsxRuntimeExports.jsx(React.Fragment, { children: renderer(shortcode.attrs, shortcode.content) }, `shortcode-${shortcode.startIndex}`));
|
|
3055
|
+
}
|
|
3056
|
+
else {
|
|
3057
|
+
// Keep original shortcode if no renderer found
|
|
3058
|
+
parts.push(shortcode.fullMatch);
|
|
3059
|
+
}
|
|
3060
|
+
lastIndex = shortcode.endIndex;
|
|
3061
|
+
}
|
|
3062
|
+
// Add remaining text
|
|
3063
|
+
if (lastIndex < text.length) {
|
|
3064
|
+
const remainingText = text.substring(lastIndex);
|
|
3065
|
+
if (remainingText) {
|
|
3066
|
+
parts.push(remainingText);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
return parts;
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3142
3072
|
/**
|
|
3143
3073
|
* Convert image URL with optional Cloudflare variant transformation
|
|
3144
3074
|
*
|