@marvalt/wparser 0.1.4 → 0.1.6

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 CHANGED
@@ -1506,63 +1506,338 @@ function renderTextWithShortcodes(text, registry) {
1506
1506
  return parts;
1507
1507
  }
1508
1508
 
1509
+ /**
1510
+ * Content extraction utilities for WordPress blocks
1511
+ * Extracts text content from various block formats
1512
+ */
1513
+ /**
1514
+ * Extract text content from a block's innerHTML by stripping HTML tags
1515
+ */
1516
+ function extractTextFromHTML(html) {
1517
+ if (!html)
1518
+ return '';
1519
+ // Remove HTML tags and decode entities
1520
+ let text = html
1521
+ .replace(/<[^>]*>/g, '') // Remove HTML tags
1522
+ .replace(/&nbsp;/g, ' ') // Replace &nbsp; with space
1523
+ .replace(/&#8217;/g, "'") // Replace apostrophe entity
1524
+ .replace(/&#8220;/g, '"') // Replace left double quote
1525
+ .replace(/&#8221;/g, '"') // Replace right double quote
1526
+ .replace(/&#8230;/g, '...') // Replace ellipsis
1527
+ .replace(/&amp;/g, '&') // Replace &amp;
1528
+ .replace(/&lt;/g, '<') // Replace &lt;
1529
+ .replace(/&gt;/g, '>') // Replace &gt;
1530
+ .replace(/&quot;/g, '"') // Replace &quot;
1531
+ .replace(/&#8211;/g, '–') // Replace en dash
1532
+ .replace(/&#8212;/g, '—') // Replace em dash
1533
+ .trim();
1534
+ // Clean up extra whitespace
1535
+ text = text.replace(/\s+/g, ' ');
1536
+ return text;
1537
+ }
1538
+ /**
1539
+ * Extract text content from block attributes or innerHTML
1540
+ */
1541
+ function getBlockTextContent(block) {
1542
+ const attrs = block.attributes || {};
1543
+ // Try various attribute keys
1544
+ const content = attrs['content'] || attrs['text'] || attrs['value'] || '';
1545
+ if (typeof content === 'string' && content.trim()) {
1546
+ return content.trim();
1547
+ }
1548
+ // Fall back to innerHTML
1549
+ if (block.innerHTML) {
1550
+ return extractTextFromHTML(block.innerHTML);
1551
+ }
1552
+ return '';
1553
+ }
1554
+ /**
1555
+ * Extract image URL from block attributes
1556
+ * Checks for Cloudflare URLs first, then falls back to regular URLs
1557
+ * Also extracts from innerHTML if needed
1558
+ */
1559
+ function getImageUrl(block) {
1560
+ const attrs = block.attributes || {};
1561
+ // Check various possible URL attributes
1562
+ let url = attrs['url'] ||
1563
+ attrs['src'] ||
1564
+ attrs['imageUrl'] ||
1565
+ attrs['mediaUrl'] ||
1566
+ attrs['backgroundImage'];
1567
+ if (typeof url === 'string' && url.trim()) {
1568
+ return url.trim();
1569
+ }
1570
+ // Try to extract from innerHTML if it's an img tag
1571
+ if (block.innerHTML) {
1572
+ // Try img src first
1573
+ const imgMatch = block.innerHTML.match(/<img[^>]+src=["']([^"']+)["']/i);
1574
+ if (imgMatch && imgMatch[1]) {
1575
+ return imgMatch[1];
1576
+ }
1577
+ // Try background-image in style attribute
1578
+ const bgMatch = block.innerHTML.match(/background-image:\s*url\(["']?([^"')]+)["']?\)/i);
1579
+ if (bgMatch && bgMatch[1]) {
1580
+ return bgMatch[1];
1581
+ }
1582
+ }
1583
+ return null;
1584
+ }
1585
+ /**
1586
+ * Extract image attributes (alt, width, height) from block
1587
+ */
1588
+ function getImageAttributes(block) {
1589
+ const attrs = block.attributes || {};
1590
+ const url = getImageUrl(block);
1591
+ return {
1592
+ url,
1593
+ alt: attrs['alt'] || '',
1594
+ width: attrs['width'] ? Number(attrs['width']) : undefined,
1595
+ height: attrs['height'] ? Number(attrs['height']) : undefined,
1596
+ };
1597
+ }
1598
+
1599
+ /**
1600
+ * Style mapping utilities
1601
+ * Maps WordPress block attributes to Tailwind CSS classes
1602
+ */
1603
+ /**
1604
+ * Map WordPress alignment to Tailwind classes
1605
+ */
1606
+ function getAlignmentClasses(align) {
1607
+ if (!align)
1608
+ return '';
1609
+ switch (align) {
1610
+ case 'full':
1611
+ return 'w-full';
1612
+ case 'wide':
1613
+ return 'max-w-7xl mx-auto';
1614
+ case 'center':
1615
+ return 'mx-auto';
1616
+ case 'left':
1617
+ return 'mr-auto';
1618
+ case 'right':
1619
+ return 'ml-auto';
1620
+ default:
1621
+ return '';
1622
+ }
1623
+ }
1624
+ /**
1625
+ * Map WordPress text alignment to Tailwind classes
1626
+ */
1627
+ function getTextAlignClasses(textAlign) {
1628
+ if (!textAlign)
1629
+ return '';
1630
+ switch (textAlign) {
1631
+ case 'center':
1632
+ return 'text-center';
1633
+ case 'left':
1634
+ return 'text-left';
1635
+ case 'right':
1636
+ return 'text-right';
1637
+ default:
1638
+ return '';
1639
+ }
1640
+ }
1641
+ /**
1642
+ * Map WordPress font size to Tailwind classes
1643
+ */
1644
+ function getFontSizeClasses(fontSize) {
1645
+ if (!fontSize)
1646
+ return '';
1647
+ // Map WordPress font sizes to Tailwind
1648
+ const sizeMap = {
1649
+ 'small': 'text-sm',
1650
+ 'medium': 'text-base',
1651
+ 'large': 'text-lg',
1652
+ 'x-large': 'text-xl',
1653
+ 'xx-large': 'text-3xl',
1654
+ 'xxx-large': 'text-4xl',
1655
+ };
1656
+ return sizeMap[fontSize] || '';
1657
+ }
1658
+ /**
1659
+ * Get container classes based on layout and alignment
1660
+ */
1661
+ function getContainerClasses(align, layout) {
1662
+ const alignClass = getAlignmentClasses(align);
1663
+ // If layout is constrained, use container
1664
+ if (layout?.type === 'constrained') {
1665
+ return align === 'full' ? 'w-full' : 'container';
1666
+ }
1667
+ return alignClass || 'container';
1668
+ }
1669
+ /**
1670
+ * Get spacing classes for sections
1671
+ */
1672
+ function getSectionSpacingClasses() {
1673
+ return 'py-16 md:py-24';
1674
+ }
1675
+ /**
1676
+ * Get content spacing classes
1677
+ */
1678
+ function getContentSpacingClasses() {
1679
+ return 'space-y-6';
1680
+ }
1681
+ /**
1682
+ * Build className string from multiple class sources
1683
+ */
1684
+ function buildClassName(...classes) {
1685
+ return classes
1686
+ .filter((cls) => Boolean(cls && cls.trim()))
1687
+ .join(' ')
1688
+ .trim();
1689
+ }
1690
+
1691
+ /**
1692
+ * Cloudflare Images URL helpers for wparser package
1693
+ * Formats Cloudflare image URLs with variants for optimal performance
1694
+ */
1695
+ /**
1696
+ * Check if a URL is a Cloudflare Images URL
1697
+ */
1698
+ const isCloudflareImageUrl = (url) => {
1699
+ if (!url)
1700
+ return false;
1701
+ try {
1702
+ const u = new URL(url);
1703
+ return u.hostname.toLowerCase().includes('imagedelivery.net');
1704
+ }
1705
+ catch {
1706
+ return false;
1707
+ }
1708
+ };
1709
+ /**
1710
+ * Append Cloudflare Images variant to the base URL if not already present.
1711
+ * Example output: https://imagedelivery.net/<account>/<id>/w=1024,h=600
1712
+ */
1713
+ const getCloudflareVariantUrl = (url, options) => {
1714
+ if (!isCloudflareImageUrl(url))
1715
+ return url;
1716
+ // If a transform already exists, return as-is
1717
+ if (/\/w=\d+/.test(url) || /\/(public|thumbnail|banner|avatar)/.test(url)) {
1718
+ return url;
1719
+ }
1720
+ const { width, height } = options;
1721
+ const hasTrailingSlash = url.endsWith('/');
1722
+ const base = hasTrailingSlash ? url.slice(0, -1) : url;
1723
+ const variant = `w=${Math.max(1, Math.floor(width))}` + (height ? `,h=${Math.max(1, Math.floor(height))}` : '');
1724
+ return `${base}/${variant}`;
1725
+ };
1726
+
1509
1727
  const Paragraph = ({ block, context }) => {
1510
- const content = getString(block);
1728
+ const content = getBlockTextContent(block);
1729
+ const attrs = block.attributes || {};
1730
+ const textAlign = getTextAlignClasses(attrs['align']);
1511
1731
  // Check if content contains shortcodes
1512
1732
  const hasShortcodes = /\[(\w+)/.test(content);
1513
1733
  if (hasShortcodes && context.registry.shortcodes) {
1514
1734
  const parts = renderTextWithShortcodes(content, context.registry);
1515
- return jsxRuntimeExports.jsx("p", { className: "prose-p", children: parts });
1735
+ return jsxRuntimeExports.jsx("p", { className: buildClassName('text-gray-700', textAlign), children: parts });
1516
1736
  }
1517
- return jsxRuntimeExports.jsx("p", { className: "prose-p", children: content });
1737
+ return jsxRuntimeExports.jsx("p", { className: buildClassName('text-gray-700', textAlign), children: content });
1518
1738
  };
1519
1739
  const Heading = ({ block, children }) => {
1520
- const { level = 2 } = block.attributes || {};
1521
- const content = getString(block);
1740
+ const attrs = block.attributes || {};
1741
+ const { level = 2 } = attrs;
1742
+ const content = getBlockTextContent(block);
1743
+ const textAlign = getTextAlignClasses(attrs['textAlign']);
1744
+ const fontSize = getFontSizeClasses(attrs['fontSize']);
1522
1745
  const Tag = `h${Math.min(Math.max(Number(level) || 2, 1), 6)}`;
1523
- return jsxRuntimeExports.jsx(Tag, { className: "prose-headings font-semibold", children: children ?? content });
1746
+ // Default heading sizes if fontSize not specified
1747
+ const sizeClass = fontSize || (level === 1 ? 'text-4xl' : level === 2 ? 'text-3xl' : level === 3 ? 'text-2xl' : 'text-xl');
1748
+ return (jsxRuntimeExports.jsx(Tag, { className: buildClassName('font-bold text-gray-900', sizeClass, textAlign), children: children ?? content }));
1524
1749
  };
1525
1750
  const Image = ({ block }) => {
1526
- const { url, alt, width, height } = block.attributes || {};
1527
- if (!url)
1751
+ const imageAttrs = getImageAttributes(block);
1752
+ if (!imageAttrs.url)
1528
1753
  return null;
1529
- return (jsxRuntimeExports.jsx("img", { src: url, alt: alt || '', width: width, height: height, className: "w-full h-auto object-cover", loading: "lazy" }));
1754
+ // Use Cloudflare variant URL if it's a Cloudflare image
1755
+ let imageUrl = imageAttrs.url;
1756
+ if (isCloudflareImageUrl(imageUrl)) {
1757
+ const width = imageAttrs.width || 1024;
1758
+ const height = imageAttrs.height;
1759
+ imageUrl = getCloudflareVariantUrl(imageUrl, { width, height });
1760
+ }
1761
+ return (jsxRuntimeExports.jsx("img", { src: imageUrl, alt: imageAttrs.alt, width: imageAttrs.width, height: imageAttrs.height, className: "w-full h-auto object-cover rounded-lg", loading: "lazy" }));
1530
1762
  };
1531
1763
  const List = ({ block, children }) => {
1532
- const { ordered } = block.attributes || {};
1764
+ const attrs = block.attributes || {};
1765
+ const { ordered } = attrs;
1533
1766
  const Tag = ordered ? 'ol' : 'ul';
1534
- return React.createElement(Tag, { className: 'list-disc pl-6 space-y-1' }, children);
1767
+ return React.createElement(Tag, { className: 'list-disc pl-6 space-y-2 text-gray-700' }, children);
1535
1768
  };
1536
1769
  const ListItem = ({ children }) => {
1537
- return jsxRuntimeExports.jsx("li", { children: children });
1770
+ return jsxRuntimeExports.jsx("li", { className: "text-gray-700", children: children });
1538
1771
  };
1539
- const Group = ({ children }) => {
1540
- return jsxRuntimeExports.jsx("div", { className: "space-y-4", children: children });
1772
+ const Group = ({ block, children }) => {
1773
+ const attrs = block.attributes || {};
1774
+ const align = attrs['align'];
1775
+ const layout = attrs['layout'];
1776
+ // Determine if this is a section-level group (has alignment) or content-level
1777
+ const isSection = align === 'full' || align === 'wide';
1778
+ const containerClass = getContainerClasses(align, layout);
1779
+ const spacingClass = isSection ? getSectionSpacingClasses() : getContentSpacingClasses();
1780
+ return (jsxRuntimeExports.jsx("div", { className: buildClassName(containerClass, spacingClass), children: children }));
1541
1781
  };
1542
- const Columns = ({ children }) => {
1543
- return jsxRuntimeExports.jsx("div", { className: "grid gap-6 md:grid-cols-2", children: children });
1782
+ const Columns = ({ block, children }) => {
1783
+ const attrs = block.attributes || {};
1784
+ const align = attrs['align'];
1785
+ const alignClass = getAlignmentClasses(align);
1786
+ return (jsxRuntimeExports.jsx("div", { className: buildClassName('grid grid-cols-1 md:grid-cols-2 gap-6 lg:gap-12', alignClass), children: children }));
1544
1787
  };
1545
- const Column = ({ children }) => {
1546
- return jsxRuntimeExports.jsx("div", { className: "space-y-4", children: children });
1788
+ const Column = ({ block, children }) => {
1789
+ const attrs = block.attributes || {};
1790
+ const width = attrs['width'];
1791
+ // Handle column width (e.g., "50%" becomes flex-basis)
1792
+ const style = width ? { flexBasis: width } : undefined;
1793
+ return (jsxRuntimeExports.jsx("div", { className: "space-y-4", style: style, children: children }));
1547
1794
  };
1548
1795
  const Separator = () => jsxRuntimeExports.jsx("hr", { className: "border-gray-200 my-8" });
1549
1796
  const ButtonBlock = ({ block }) => {
1550
- const { url, text } = block.attributes || {};
1551
- if (!url)
1797
+ const attrs = block.attributes || {};
1798
+ const url = attrs['url'];
1799
+ const text = attrs['text'];
1800
+ attrs['linkDestination'];
1801
+ if (!url && !text)
1552
1802
  return null;
1553
- return (jsxRuntimeExports.jsx("a", { href: url, className: "inline-flex items-center rounded-md bg-primary px-4 py-2 text-white", children: text || 'Learn more' }));
1803
+ const buttonText = text || getBlockTextContent(block) || 'Learn more';
1804
+ // Handle internal vs external links
1805
+ const isExternal = url && (url.startsWith('http://') || url.startsWith('https://'));
1806
+ const linkProps = isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {};
1807
+ return (jsxRuntimeExports.jsx("a", { href: url || '#', className: "inline-flex items-center justify-center rounded-md bg-primary px-6 py-3 text-white font-medium hover:bg-primary/90 transition-colors", ...linkProps, children: buttonText }));
1554
1808
  };
1555
1809
  const Cover = ({ block, children }) => {
1556
1810
  const attrs = block.attributes || {};
1557
- const { url, backgroundImage, overlayColor, dimRatio = 0, align = 'full', minHeight, hasParallax, } = attrs;
1811
+ const { url, id, backgroundImage, overlayColor, dimRatio = 0, align = 'full', minHeight, minHeightUnit = 'vh', hasParallax, } = attrs;
1558
1812
  // Get background image URL from various possible sources
1559
- const bgImageUrl = url || backgroundImage || (typeof backgroundImage === 'object' && backgroundImage?.url);
1813
+ let bgImageUrl = url || backgroundImage || (typeof backgroundImage === 'object' && backgroundImage?.url);
1814
+ // If not found in attributes, try to extract from innerHTML
1815
+ if (!bgImageUrl && block.innerHTML) {
1816
+ // Try to extract img src from innerHTML
1817
+ const imgMatch = block.innerHTML.match(/<img[^>]+src=["']([^"']+)["']/i);
1818
+ if (imgMatch && imgMatch[1]) {
1819
+ bgImageUrl = imgMatch[1];
1820
+ }
1821
+ // Try background-image in style attribute
1822
+ if (!bgImageUrl) {
1823
+ const bgMatch = block.innerHTML.match(/background-image:\s*url\(["']?([^"')]+)["']?\)/i);
1824
+ if (bgMatch && bgMatch[1]) {
1825
+ bgImageUrl = bgMatch[1];
1826
+ }
1827
+ }
1828
+ }
1829
+ // Convert to Cloudflare URL if it's a Cloudflare image, otherwise use as-is
1830
+ if (bgImageUrl && isCloudflareImageUrl(bgImageUrl)) {
1831
+ // Use full width for cover images
1832
+ bgImageUrl = getCloudflareVariantUrl(bgImageUrl, { width: 1920 });
1833
+ }
1560
1834
  // Build alignment classes
1561
- const alignClass = align === 'full' ? 'w-full' : align === 'wide' ? 'max-w-7xl mx-auto' : '';
1835
+ const alignClass = getAlignmentClasses(align);
1562
1836
  // Build style object
1563
1837
  const style = {};
1564
1838
  if (minHeight) {
1565
- style.minHeight = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;
1839
+ const minHeightValue = typeof minHeight === 'number' ? minHeight : parseFloat(String(minHeight));
1840
+ style.minHeight = minHeightUnit === 'vh' ? `${minHeightValue}vh` : `${minHeightValue}px`;
1566
1841
  }
1567
1842
  if (bgImageUrl) {
1568
1843
  style.backgroundImage = `url(${bgImageUrl})`;
@@ -1573,11 +1848,11 @@ const Cover = ({ block, children }) => {
1573
1848
  }
1574
1849
  }
1575
1850
  // Calculate overlay opacity
1576
- const overlayOpacity = dimRatio / 100;
1577
- return (jsxRuntimeExports.jsxs("div", { className: `relative ${alignClass}`, style: style, children: [overlayOpacity > 0 && (jsxRuntimeExports.jsx("span", { className: "absolute inset-0", style: {
1578
- backgroundColor: overlayColor || '#000000',
1851
+ const overlayOpacity = typeof dimRatio === 'number' ? dimRatio / 100 : 0;
1852
+ return (jsxRuntimeExports.jsxs("div", { className: buildClassName('relative w-full', alignClass), style: style, children: [overlayOpacity > 0 && (jsxRuntimeExports.jsx("span", { className: "absolute inset-0 z-0", style: {
1853
+ backgroundColor: overlayColor === 'contrast' ? '#000000' : (overlayColor || '#000000'),
1579
1854
  opacity: overlayOpacity,
1580
- }, "aria-hidden": "true" })), jsxRuntimeExports.jsx("div", { className: "relative z-10 container mx-auto px-4 py-12", children: children })] }));
1855
+ }, "aria-hidden": "true" })), jsxRuntimeExports.jsx("div", { className: buildClassName('relative z-10', align === 'full' ? 'w-full' : 'container mx-auto px-4'), children: children })] }));
1581
1856
  };
1582
1857
  const MediaText = ({ block, children, context }) => {
1583
1858
  const attrs = block.attributes || {};
@@ -1594,7 +1869,7 @@ const MediaText = ({ block, children, context }) => {
1594
1869
  // Content is all other children
1595
1870
  const contentElements = childrenArray.filter((_, index) => index !== mediaBlockIndex);
1596
1871
  // Build alignment classes
1597
- const alignClass = align === 'full' ? 'w-full' : align === 'wide' ? 'max-w-7xl mx-auto' : 'max-w-6xl mx-auto';
1872
+ const alignClass = getAlignmentClasses(align) || 'max-w-7xl mx-auto';
1598
1873
  // Vertical alignment classes
1599
1874
  const verticalAlignClass = verticalAlignment === 'top' ? 'items-start' :
1600
1875
  verticalAlignment === 'bottom' ? 'items-end' :
@@ -1603,7 +1878,7 @@ const MediaText = ({ block, children, context }) => {
1603
1878
  const stackClass = 'flex-col md:flex-row';
1604
1879
  // Media position determines order
1605
1880
  const isMediaRight = mediaPosition === 'right';
1606
- return (jsxRuntimeExports.jsx("div", { className: `${alignClass} px-4`, children: jsxRuntimeExports.jsxs("div", { className: `flex ${stackClass} ${verticalAlignClass} gap-6`, children: [jsxRuntimeExports.jsx("div", { className: `${isMediaRight ? 'order-2' : 'order-1'} ${imageFill ? 'w-full md:w-1/2' : 'flex-shrink-0'}`, children: mediaElement || jsxRuntimeExports.jsx("div", { className: "bg-gray-200 h-64 rounded" }) }), jsxRuntimeExports.jsx("div", { className: `${isMediaRight ? 'order-1' : 'order-2'} ${imageFill ? 'w-full md:w-1/2' : 'flex-1'} space-y-4`, children: contentElements.length > 0 ? contentElements : children })] }) }));
1881
+ return (jsxRuntimeExports.jsx("div", { className: buildClassName(alignClass, 'px-4'), children: jsxRuntimeExports.jsxs("div", { className: buildClassName('flex', stackClass, verticalAlignClass, 'gap-6 lg:gap-12'), children: [jsxRuntimeExports.jsx("div", { className: buildClassName(isMediaRight ? 'order-2' : 'order-1', imageFill ? 'w-full md:w-1/2' : 'flex-shrink-0'), children: mediaElement || jsxRuntimeExports.jsx("div", { className: "bg-gray-200 h-64 rounded-lg" }) }), jsxRuntimeExports.jsx("div", { className: buildClassName(isMediaRight ? 'order-1' : 'order-2', imageFill ? 'w-full md:w-1/2' : 'flex-1', 'space-y-4'), children: contentElements.length > 0 ? contentElements : children })] }) }));
1607
1882
  };
1608
1883
  const Fallback = ({ block, children }) => {
1609
1884
  // Minimal fallback; do not render innerHTML directly in v1 for safety
@@ -1621,7 +1896,15 @@ function createDefaultRegistry() {
1621
1896
  'core/column': Column,
1622
1897
  'core/separator': Separator,
1623
1898
  'core/button': ButtonBlock,
1624
- 'core/buttons': ({ children }) => jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-3", children: children }),
1899
+ 'core/buttons': ({ block, children }) => {
1900
+ const attrs = block.attributes || {};
1901
+ const layout = attrs['layout'];
1902
+ const justifyContent = layout?.justifyContent || 'left';
1903
+ const justifyClass = justifyContent === 'center' ? 'justify-center' :
1904
+ justifyContent === 'right' ? 'justify-end' :
1905
+ 'justify-start';
1906
+ return jsxRuntimeExports.jsx("div", { className: buildClassName('flex flex-wrap gap-3', justifyClass), children: children });
1907
+ },
1625
1908
  'core/quote': ({ children }) => jsxRuntimeExports.jsx("blockquote", { className: "border-l-4 pl-4 italic", children: children }),
1626
1909
  'core/code': ({ block }) => (jsxRuntimeExports.jsx("pre", { className: "bg-gray-100 p-3 rounded text-sm overflow-auto", children: jsxRuntimeExports.jsx("code", { children: getString(block) }) })),
1627
1910
  'core/preformatted': ({ block }) => jsxRuntimeExports.jsx("pre", { children: getString(block) }),
@@ -1645,12 +1928,9 @@ function createDefaultRegistry() {
1645
1928
  fallback: Fallback,
1646
1929
  };
1647
1930
  }
1931
+ // Legacy function for backward compatibility - use getBlockTextContent instead
1648
1932
  function getString(block) {
1649
- const attrs = (block.attributes || {});
1650
- const content = attrs['content'];
1651
- const text = attrs['text'];
1652
- const value = (typeof content === 'string' ? content : typeof text === 'string' ? text : block.innerHTML || '');
1653
- return String(value);
1933
+ return getBlockTextContent(block);
1654
1934
  }
1655
1935
 
1656
1936
  const WPContent = ({ blocks, registry, className }) => {
@@ -1686,7 +1966,7 @@ const WPPage = ({ page, registry, className }) => {
1686
1966
  }
1687
1967
  const hasHeroShortcode = React.useMemo(() => detectHeroShortcode(page.blocks), [page.blocks]);
1688
1968
  const featured = getFeaturedImage(page);
1689
- return (jsxRuntimeExports.jsxs("article", { className: className, children: [!hasHeroShortcode && featured && (jsxRuntimeExports.jsx(HeroFromFeatured, { featured: featured, title: page.title?.rendered })), jsxRuntimeExports.jsxs("div", { className: "container mx-auto px-4 py-8 prose max-w-none", children: [jsxRuntimeExports.jsx("header", { className: "mb-8", children: page.title?.rendered && (jsxRuntimeExports.jsx("h1", { className: "text-3xl font-bold", children: page.title.rendered })) }), jsxRuntimeExports.jsx(WPContent, { blocks: page.blocks, registry: registry })] })] }));
1969
+ return (jsxRuntimeExports.jsxs("article", { className: className, children: [!hasHeroShortcode && featured && (jsxRuntimeExports.jsx(HeroFromFeatured, { featured: featured, title: page.title?.rendered })), jsxRuntimeExports.jsx(WPContent, { blocks: page.blocks, registry: registry })] }));
1690
1970
  };
1691
1971
  function detectHeroShortcode(blocks) {
1692
1972
  for (const block of blocks) {
@@ -1736,8 +2016,21 @@ class WPErrorBoundary extends React.Component {
1736
2016
  exports.WPContent = WPContent;
1737
2017
  exports.WPErrorBoundary = WPErrorBoundary;
1738
2018
  exports.WPPage = WPPage;
2019
+ exports.buildClassName = buildClassName;
1739
2020
  exports.createDefaultRegistry = createDefaultRegistry;
2021
+ exports.extractTextFromHTML = extractTextFromHTML;
1740
2022
  exports.findShortcodes = findShortcodes;
2023
+ exports.getAlignmentClasses = getAlignmentClasses;
2024
+ exports.getBlockTextContent = getBlockTextContent;
2025
+ exports.getCloudflareVariantUrl = getCloudflareVariantUrl;
2026
+ exports.getContainerClasses = getContainerClasses;
2027
+ exports.getContentSpacingClasses = getContentSpacingClasses;
2028
+ exports.getFontSizeClasses = getFontSizeClasses;
2029
+ exports.getImageAttributes = getImageAttributes;
2030
+ exports.getImageUrl = getImageUrl;
2031
+ exports.getSectionSpacingClasses = getSectionSpacingClasses;
2032
+ exports.getTextAlignClasses = getTextAlignClasses;
2033
+ exports.isCloudflareImageUrl = isCloudflareImageUrl;
1741
2034
  exports.parseGutenbergBlocks = parseGutenbergBlocks;
1742
2035
  exports.parseShortcodeAttrs = parseShortcodeAttrs;
1743
2036
  exports.renderNodes = renderNodes;