@emdash-cms/gutenberg-to-portable-text 0.0.1 → 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License
2
+
3
+ Copyright 2026 Cloudflare Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/transformers/index.ts","../src/transformers/core.ts","../src/transformers/embed.ts","../src/inline.ts","../src/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/transformers/index.ts","../src/transformers/core.ts","../src/transformers/embed.ts","../src/inline.ts","../src/index.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -682,7 +682,6 @@ const group = (block, _options, context) => {
682
682
  * core/table → table block
683
683
  */
684
684
  const table = (block, _options, context) => {
685
- attrBoolean(block.attrs, "hasFixedLayout");
686
685
  const tableMatch = block.innerHTML.match(TABLE_TAG_PATTERN);
687
686
  if (!tableMatch) return [];
688
687
  const tableContent = tableMatch[1];
@@ -826,7 +825,7 @@ const cover = (block, _options, context) => {
826
825
  const overlayColor = attrString(block.attrs, "overlayColor");
827
826
  const customOverlayColor = attrString(block.attrs, "customOverlayColor");
828
827
  const dimRatio = attrNumber(block.attrs, "dimRatio");
829
- const minHeight = attrString(block.attrs, "minHeight");
828
+ const minHeight = attrNumber(block.attrs, "minHeight");
830
829
  const minHeightUnit = attrString(block.attrs, "minHeightUnit");
831
830
  const contentPosition = attrString(block.attrs, "contentPosition");
832
831
  const content = context.transformBlocks(block.innerBlocks);
@@ -835,7 +834,7 @@ const cover = (block, _options, context) => {
835
834
  else if (contentPosition?.includes("right")) alignment = "right";
836
835
  else if (contentPosition?.includes("center")) alignment = "center";
837
836
  let minHeightStr;
838
- if (minHeight) minHeightStr = minHeightUnit ? `${minHeight}${minHeightUnit}` : `${minHeight}px`;
837
+ if (minHeight !== void 0) minHeightStr = minHeightUnit ? `${minHeight}${minHeightUnit}` : `${minHeight}px`;
839
838
  return [{
840
839
  _type: "cover",
841
840
  _key: context.generateKey(),
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["decodeUrlEntities","AMP_ENTITY_PATTERN","APOS_ENTITY_PATTERN","NBSP_ENTITY_PATTERN","decodeHtmlEntities","core.paragraph","core.heading","core.list","core.quote","core.code","core.preformatted","core.pullquote","core.verse","core.image","core.gallery","core.file","core.mediaText","core.cover","core.columns","core.group","core.separator","core.table","core.buttons","core.button","core.more","core.nextpage","core.html","core.shortcode","embed.embed","embed.video","embed.audio","embed.youtube","embed.twitter","embed.vimeo"],"sources":["../src/url.ts","../src/inline.ts","../src/types.ts","../src/transformers/core.ts","../src/transformers/embed.ts","../src/transformers/index.ts","../src/index.ts"],"sourcesContent":["/**\n * URL scheme validation for the converter pipeline (defense-in-depth).\n *\n * This mirrors the canonical sanitizeHref in packages/core/src/utils/url.ts.\n * The converter is a standalone zero-dependency package, so it carries its own\n * copy. The render layer in core is the primary defense; this is secondary.\n */\n\nconst SAFE_URL_SCHEME_RE = /^(https?:|mailto:|tel:|\\/(?!\\/)|#)/i;\n\n/**\n * Returns the URL unchanged if it uses a safe scheme, otherwise returns \"\".\n *\n * Returns empty string (not \"#\") because this is the converter layer — we\n * strip bad URLs rather than substituting anchors. The render layer handles\n * the fallback to \"#\".\n */\nexport function sanitizeHref(url: string | undefined | null): string {\n\tif (!url) return \"\";\n\treturn SAFE_URL_SCHEME_RE.test(url) ? url : \"\";\n}\n","/**\n * Inline HTML to Portable Text spans converter\n *\n * Parses inline HTML elements (strong, em, a, code, etc.) and converts\n * them to Portable Text spans with marks.\n */\n\nimport { parseFragment, type DefaultTreeAdapterMap } from \"parse5\";\n\nimport type { PortableTextSpan, PortableTextMarkDef } from \"./types.js\";\nimport { sanitizeHref } from \"./url.js\";\n\n// Regex patterns for inline parsing\nconst WHITESPACE_PATTERN = /\\S/;\n\n// Pre-compiled block tag patterns\nconst BLOCK_TAG_PATTERNS: Record<string, { open: RegExp; close: RegExp }> = {\n\tp: { open: /^<p[^>]*>/i, close: /<\\/p>$/i },\n\th1: { open: /^<h1[^>]*>/i, close: /<\\/h1>$/i },\n\th2: { open: /^<h2[^>]*>/i, close: /<\\/h2>$/i },\n\th3: { open: /^<h3[^>]*>/i, close: /<\\/h3>$/i },\n\th4: { open: /^<h4[^>]*>/i, close: /<\\/h4>$/i },\n\th5: { open: /^<h5[^>]*>/i, close: /<\\/h5>$/i },\n\th6: { open: /^<h6[^>]*>/i, close: /<\\/h6>$/i },\n\tli: { open: /^<li[^>]*>/i, close: /<\\/li>$/i },\n\tblockquote: { open: /^<blockquote[^>]*>/i, close: /<\\/blockquote>$/i },\n\tfigcaption: { open: /^<figcaption[^>]*>/i, close: /<\\/figcaption>$/i },\n};\n\n// Regex patterns for extracting attributes\nconst IMG_ALT_PATTERN = /<img[^>]+alt=[\"']([^\"']*)[\"']/i;\nconst FIGCAPTION_PATTERN = /<figcaption[^>]*>([\\s\\S]*?)<\\/figcaption>/i;\nconst IMG_SRC_PATTERN = /<img[^>]+src=[\"']([^\"']*)[\"']/i;\nconst URL_AMP_ENTITY_PATTERN = /&amp;/g;\nconst URL_NUMERIC_AMP_ENTITY_PATTERN = /&#0?38;/g;\nconst URL_HEX_AMP_ENTITY_PATTERN = /&#x26;/gi;\n\ntype Node = DefaultTreeAdapterMap[\"node\"];\ntype TextNode = DefaultTreeAdapterMap[\"textNode\"];\ntype Element = DefaultTreeAdapterMap[\"element\"];\n\ninterface ParseResult {\n\tchildren: PortableTextSpan[];\n\tmarkDefs: PortableTextMarkDef[];\n}\n\n/**\n * Parse inline HTML content into Portable Text spans\n */\nexport function parseInlineContent(html: string, generateKey: () => string): ParseResult {\n\tconst children: PortableTextSpan[] = [];\n\tconst markDefs: PortableTextMarkDef[] = [];\n\tconst markDefMap = new Map<string, string>();\n\n\t// Handle whitespace-only input BEFORE stripping (parse5 normalizes whitespace away)\n\tif (html.length > 0 && !WHITESPACE_PATTERN.test(html)) {\n\t\treturn {\n\t\t\tchildren: [{ _type: \"span\", _key: generateKey(), text: html }],\n\t\t\tmarkDefs: [],\n\t\t};\n\t}\n\n\t// Strip wrapping tags like <p>, <h1>, etc.\n\tconst strippedHtml = stripBlockTags(html);\n\n\t// Parse HTML fragment\n\tconst fragment = parseFragment(strippedHtml);\n\n\t// Walk the tree and build spans\n\twalkNodes(fragment.childNodes, [], children, markDefs, markDefMap, generateKey);\n\n\t// Ensure at least one span exists\n\tif (children.length === 0) {\n\t\tchildren.push({\n\t\t\t_type: \"span\",\n\t\t\t_key: generateKey(),\n\t\t\ttext: \"\",\n\t\t});\n\t}\n\n\treturn { children, markDefs };\n}\n\n/**\n * Strip common block-level wrapper tags\n */\nfunction stripBlockTags(html: string): string {\n\t// Remove leading/trailing whitespace\n\tlet stripped = html.trim();\n\n\t// Strip common block wrappers\n\tconst blockTags = [\"p\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"li\", \"blockquote\", \"figcaption\"];\n\n\tfor (const tag of blockTags) {\n\t\tconst patterns = BLOCK_TAG_PATTERNS[tag];\n\t\tif (patterns && patterns.open.test(stripped) && patterns.close.test(stripped)) {\n\t\t\tstripped = stripped.replace(patterns.open, \"\").replace(patterns.close, \"\").trim();\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn stripped;\n}\n\n/**\n * Recursively walk DOM nodes and build spans\n */\nfunction walkNodes(\n\tnodes: Node[],\n\tcurrentMarks: string[],\n\tchildren: PortableTextSpan[],\n\tmarkDefs: PortableTextMarkDef[],\n\tmarkDefMap: Map<string, string>,\n\tgenerateKey: () => string,\n): void {\n\tfor (const node of nodes) {\n\t\tif (isTextNode(node)) {\n\t\t\tconst text = node.value;\n\t\t\tif (text) {\n\t\t\t\t// Handle line breaks in text\n\t\t\t\tconst parts = text.split(\"\\n\");\n\t\t\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\t\t\tconst part = parts[i];\n\t\t\t\t\tif (part || i > 0) {\n\t\t\t\t\t\t// Add text span\n\t\t\t\t\t\tif (part) {\n\t\t\t\t\t\t\tchildren.push({\n\t\t\t\t\t\t\t\t_type: \"span\",\n\t\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\t\ttext: part,\n\t\t\t\t\t\t\t\tmarks: currentMarks.length > 0 ? [...currentMarks] : undefined,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Add newline (except after last part)\n\t\t\t\t\t\tif (i < parts.length - 1) {\n\t\t\t\t\t\t\t// Append newline to previous span or create new one\n\t\t\t\t\t\t\tif (children.length > 0) {\n\t\t\t\t\t\t\t\tconst lastChild = children.at(-1);\n\t\t\t\t\t\t\t\tif (lastChild) {\n\t\t\t\t\t\t\t\t\tlastChild.text += \"\\n\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tchildren.push({\n\t\t\t\t\t\t\t\t\t_type: \"span\",\n\t\t\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\t\t\ttext: \"\\n\",\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (isElement(node)) {\n\t\t\tconst tagName = node.tagName.toLowerCase();\n\n\t\t\t// Handle <br> as newline\n\t\t\tif (tagName === \"br\") {\n\t\t\t\tif (children.length > 0) {\n\t\t\t\t\tconst lastChild = children.at(-1);\n\t\t\t\t\tif (lastChild) {\n\t\t\t\t\t\tlastChild.text += \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tchildren.push({\n\t\t\t\t\t\t_type: \"span\",\n\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\ttext: \"\\n\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Get mark for this element\n\t\t\tconst markResult = getMarkForElement(node, markDefs, markDefMap, generateKey);\n\t\t\tconst newMarks = markResult ? [...currentMarks, markResult] : currentMarks;\n\n\t\t\t// Recurse into children\n\t\t\twalkNodes(node.childNodes, newMarks, children, markDefs, markDefMap, generateKey);\n\t\t}\n\t}\n}\n\n/**\n * Get the Portable Text mark for an HTML element\n */\nfunction getMarkForElement(\n\telement: Element,\n\tmarkDefs: PortableTextMarkDef[],\n\tmarkDefMap: Map<string, string>,\n\tgenerateKey: () => string,\n): string | null {\n\tconst tagName = element.tagName.toLowerCase();\n\n\tswitch (tagName) {\n\t\tcase \"strong\":\n\t\tcase \"b\":\n\t\t\treturn \"strong\";\n\n\t\tcase \"em\":\n\t\tcase \"i\":\n\t\t\treturn \"em\";\n\n\t\tcase \"u\":\n\t\t\treturn \"underline\";\n\n\t\tcase \"s\":\n\t\tcase \"strike\":\n\t\tcase \"del\":\n\t\t\treturn \"strike-through\";\n\n\t\tcase \"code\":\n\t\t\treturn \"code\";\n\n\t\tcase \"sup\":\n\t\t\treturn \"superscript\";\n\n\t\tcase \"sub\":\n\t\t\treturn \"subscript\";\n\n\t\tcase \"a\": {\n\t\t\tconst href = sanitizeHref(getAttr(element, \"href\"));\n\t\t\tconst target = getAttr(element, \"target\");\n\n\t\t\t// Check if we already have a markDef for this href\n\t\t\tconst existingKey = markDefMap.get(href);\n\t\t\tif (existingKey) {\n\t\t\t\treturn existingKey;\n\t\t\t}\n\n\t\t\t// Create new mark definition\n\t\t\tconst key = generateKey();\n\t\t\tconst markDef: PortableTextMarkDef = {\n\t\t\t\t_type: \"link\",\n\t\t\t\t_key: key,\n\t\t\t\thref,\n\t\t\t};\n\t\t\tif (target === \"_blank\") {\n\t\t\t\tmarkDef.blank = true;\n\t\t\t}\n\t\t\tmarkDefs.push(markDef);\n\t\t\tmarkDefMap.set(href, key);\n\t\t\treturn key;\n\t\t}\n\n\t\tdefault:\n\t\t\t// Unknown inline element - ignore the tag, process children\n\t\t\treturn null;\n\t}\n}\n\n/**\n * Get attribute value from element\n */\nfunction getAttr(element: Element, name: string): string | undefined {\n\tconst attr = element.attrs.find((a) => a.name.toLowerCase() === name);\n\treturn attr?.value;\n}\n\n/**\n * Type guard for text nodes\n */\nfunction isTextNode(node: Node): node is TextNode {\n\treturn node.nodeName === \"#text\";\n}\n\n/**\n * Type guard for elements\n */\nfunction isElement(node: Node): node is Element {\n\treturn \"tagName\" in node;\n}\n\n/**\n * Extract plain text from HTML (for alt text, captions)\n */\nexport function extractText(html: string): string {\n\tconst fragment = parseFragment(html);\n\treturn getTextContent(fragment.childNodes);\n}\n\nfunction getTextContent(nodes: Node[]): string {\n\tlet text = \"\";\n\tfor (const node of nodes) {\n\t\tif (isTextNode(node)) {\n\t\t\ttext += node.value;\n\t\t} else if (isElement(node)) {\n\t\t\ttext += getTextContent(node.childNodes);\n\t\t}\n\t}\n\treturn text.trim();\n}\n\n/**\n * Extract alt text from an img element in HTML\n */\nexport function extractAlt(html: string): string | undefined {\n\tconst match = html.match(IMG_ALT_PATTERN);\n\tif (match) {\n\t\treturn match[1]; // Can be empty string \"\"\n\t}\n\treturn undefined;\n}\n\n/**\n * Extract caption from a figcaption element\n */\nexport function extractCaption(html: string): string | undefined {\n\tconst match = html.match(FIGCAPTION_PATTERN);\n\tif (match?.[1]) {\n\t\treturn extractText(match[1]);\n\t}\n\treturn undefined;\n}\n\n/**\n * Extract src from an img element\n */\nexport function extractSrc(html: string): string | undefined {\n\tconst match = html.match(IMG_SRC_PATTERN);\n\tif (!match?.[1]) return undefined;\n\t// Decode HTML entities in URLs\n\treturn decodeUrlEntities(match[1]);\n}\n\n/**\n * Decode HTML entities commonly found in URLs\n */\nfunction decodeUrlEntities(url: string): string {\n\treturn url\n\t\t.replace(URL_AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(URL_NUMERIC_AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(URL_HEX_AMP_ENTITY_PATTERN, \"&\");\n}\n","/**\n * Types for Gutenberg to Portable Text conversion\n */\n\n/**\n * Gutenberg block as parsed by @wordpress/block-serialization-default-parser\n */\nexport interface GutenbergBlock {\n\t/** Block name like \"core/paragraph\" or null for freeform HTML */\n\tblockName: string | null;\n\t/** Block attributes from the JSON comment */\n\tattrs: Record<string, unknown>;\n\t/** Inner HTML content */\n\tinnerHTML: string;\n\t/** Nested blocks (for columns, groups, etc.) */\n\tinnerBlocks: GutenbergBlock[];\n\t/** Content parts between inner blocks */\n\tinnerContent: Array<string | null>;\n}\n\n/**\n * Portable Text span (inline text with marks)\n */\nexport interface PortableTextSpan {\n\t_type: \"span\";\n\t_key: string;\n\ttext: string;\n\tmarks?: string[];\n}\n\n/**\n * Portable Text mark definition (for links, annotations)\n */\nexport interface PortableTextMarkDef {\n\t_type: string;\n\t_key: string;\n\t[key: string]: unknown;\n}\n\n/**\n * Portable Text text block\n */\nexport interface PortableTextTextBlock {\n\t_type: \"block\";\n\t_key: string;\n\tstyle?: \"normal\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"blockquote\";\n\tlistItem?: \"bullet\" | \"number\";\n\tlevel?: number;\n\tchildren: PortableTextSpan[];\n\tmarkDefs?: PortableTextMarkDef[];\n}\n\n/**\n * Portable Text image block\n */\nexport interface PortableTextImageBlock {\n\t_type: \"image\";\n\t_key: string;\n\tasset: {\n\t\t_type: \"reference\";\n\t\t_ref: string;\n\t\turl?: string;\n\t};\n\talt?: string;\n\tcaption?: string;\n\talignment?: \"left\" | \"center\" | \"right\" | \"wide\" | \"full\";\n\tlink?: string;\n}\n\n/**\n * Portable Text code block\n */\nexport interface PortableTextCodeBlock {\n\t_type: \"code\";\n\t_key: string;\n\tcode: string;\n\tlanguage?: string;\n}\n\n/**\n * Portable Text embed block (YouTube, Twitter, etc.)\n */\nexport interface PortableTextEmbedBlock {\n\t_type: \"embed\";\n\t_key: string;\n\turl: string;\n\tprovider?: string;\n\thtml?: string;\n}\n\n/**\n * Portable Text gallery block\n */\nexport interface PortableTextGalleryBlock {\n\t_type: \"gallery\";\n\t_key: string;\n\timages: Array<{\n\t\t_type: \"image\";\n\t\t_key: string;\n\t\tasset: { _type: \"reference\"; _ref: string; url?: string };\n\t\talt?: string;\n\t\tcaption?: string;\n\t}>;\n\tcolumns?: number;\n}\n\n/**\n * Portable Text columns block\n */\nexport interface PortableTextColumnsBlock {\n\t_type: \"columns\";\n\t_key: string;\n\tcolumns: Array<{\n\t\t_type: \"column\";\n\t\t_key: string;\n\t\tcontent: PortableTextBlock[];\n\t}>;\n}\n\n/**\n * Portable Text break/divider block\n */\nexport interface PortableTextBreakBlock {\n\t_type: \"break\";\n\t_key: string;\n\tstyle: \"lineBreak\";\n}\n\n/**\n * Portable Text table block\n */\nexport interface PortableTextTableBlock {\n\t_type: \"table\";\n\t_key: string;\n\trows: Array<{\n\t\t_type: \"tableRow\";\n\t\t_key: string;\n\t\tcells: Array<{\n\t\t\t_type: \"tableCell\";\n\t\t\t_key: string;\n\t\t\tcontent: PortableTextSpan[];\n\t\t\tmarkDefs?: PortableTextMarkDef[];\n\t\t\tisHeader?: boolean;\n\t\t}>;\n\t}>;\n\thasHeaderRow?: boolean;\n}\n\n/**\n * Fallback HTML block for unconvertible content\n */\nexport interface PortableTextHtmlBlock {\n\t_type: \"htmlBlock\";\n\t_key: string;\n\thtml: string;\n\toriginalBlockName?: string | null;\n\toriginalAttrs?: Record<string, unknown>;\n}\n\n/**\n * Portable Text button block\n */\nexport interface PortableTextButtonBlock {\n\t_type: \"button\";\n\t_key: string;\n\ttext: string;\n\turl?: string;\n\tstyle?: \"default\" | \"outline\" | \"fill\";\n}\n\n/**\n * Portable Text buttons container block\n */\nexport interface PortableTextButtonsBlock {\n\t_type: \"buttons\";\n\t_key: string;\n\tbuttons: PortableTextButtonBlock[];\n\tlayout?: \"horizontal\" | \"vertical\";\n}\n\n/**\n * Portable Text cover block (image/video with text overlay)\n */\nexport interface PortableTextCoverBlock {\n\t_type: \"cover\";\n\t_key: string;\n\tbackgroundImage?: string;\n\tbackgroundVideo?: string;\n\toverlayColor?: string;\n\toverlayOpacity?: number;\n\tcontent: PortableTextBlock[];\n\tminHeight?: string;\n\talignment?: \"left\" | \"center\" | \"right\";\n}\n\n/**\n * Portable Text file download block\n */\nexport interface PortableTextFileBlock {\n\t_type: \"file\";\n\t_key: string;\n\turl: string;\n\tfilename?: string;\n\tshowDownloadButton?: boolean;\n}\n\n/**\n * Portable Text pullquote block\n */\nexport interface PortableTextPullquoteBlock {\n\t_type: \"pullquote\";\n\t_key: string;\n\ttext: string;\n\tcitation?: string;\n}\n\n/**\n * Union of all Portable Text block types\n */\nexport type PortableTextBlock =\n\t| PortableTextTextBlock\n\t| PortableTextImageBlock\n\t| PortableTextCodeBlock\n\t| PortableTextEmbedBlock\n\t| PortableTextGalleryBlock\n\t| PortableTextColumnsBlock\n\t| PortableTextBreakBlock\n\t| PortableTextTableBlock\n\t| PortableTextHtmlBlock\n\t| PortableTextButtonBlock\n\t| PortableTextButtonsBlock\n\t| PortableTextCoverBlock\n\t| PortableTextFileBlock\n\t| PortableTextPullquoteBlock;\n\n/**\n * Options for the conversion\n */\nexport interface ConvertOptions {\n\t/** Map of WordPress media IDs to EmDash media IDs/URLs */\n\tmediaMap?: Map<number, string>;\n\t/** Custom block transformers */\n\tcustomTransformers?: Record<string, BlockTransformer>;\n\t/** Whether to generate keys (default: true) */\n\tgenerateKeys?: boolean;\n\t/** Custom key generator */\n\tkeyGenerator?: () => string;\n}\n\n/**\n * Block transformer function\n */\nexport type BlockTransformer = (\n\tblock: GutenbergBlock,\n\toptions: ConvertOptions,\n\tcontext: TransformContext,\n) => PortableTextBlock[];\n\n/**\n * Context passed to transformers\n */\nexport interface TransformContext {\n\t/** Transform child blocks recursively */\n\ttransformBlocks: (blocks: GutenbergBlock[]) => PortableTextBlock[];\n\t/** Parse inline HTML to spans */\n\tparseInlineContent: (html: string) => {\n\t\tchildren: PortableTextSpan[];\n\t\tmarkDefs: PortableTextMarkDef[];\n\t};\n\t/** Generate a unique key */\n\tgenerateKey: () => string;\n}\n\n// ── Attribute accessor helpers ──────────────────────────────────────\n// Gutenberg attrs are Record<string, unknown>. These narrow safely\n// without `as` casts.\n\n/** Extract a string attribute, returning undefined if missing or wrong type */\nexport function attrString(attrs: Record<string, unknown>, key: string): string | undefined {\n\tconst v = attrs[key];\n\treturn typeof v === \"string\" ? v : undefined;\n}\n\n/** Extract a number attribute, returning undefined if missing or wrong type */\nexport function attrNumber(attrs: Record<string, unknown>, key: string): number | undefined {\n\tconst v = attrs[key];\n\treturn typeof v === \"number\" ? v : undefined;\n}\n\n/** Extract a boolean attribute, returning undefined if missing or wrong type */\nexport function attrBoolean(attrs: Record<string, unknown>, key: string): boolean | undefined {\n\tconst v = attrs[key];\n\treturn typeof v === \"boolean\" ? v : undefined;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n\treturn typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n/** Extract an object attribute, returning undefined if missing or wrong type */\nexport function attrObject(\n\tattrs: Record<string, unknown>,\n\tkey: string,\n): Record<string, unknown> | undefined {\n\tconst v = attrs[key];\n\treturn isRecord(v) ? v : undefined;\n}\n","/**\n * Transformers for WordPress core/* blocks\n */\n\nimport { extractAlt, extractCaption, extractSrc, extractText } from \"../inline.js\";\nimport type {\n\tGutenbergBlock,\n\tPortableTextBlock,\n\tPortableTextTextBlock,\n\tBlockTransformer,\n\tTransformContext,\n} from \"../types.js\";\nimport { attrString, attrNumber, attrBoolean, attrObject } from \"../types.js\";\nimport { sanitizeHref } from \"../url.js\";\n\n// Regex patterns for core block transformers\nconst UOL_TAG_PATTERN = /<[uo]l[^>]*>([\\s\\S]*)<\\/[uo]l>/i;\nconst LI_TAG_PATTERN = /<li[^>]*>([\\s\\S]*?)<\\/li>/i;\nconst UL_TAG_PATTERN = /<ul[^>]*>([\\s\\S]*)<\\/ul>/i;\nconst OL_TAG_PATTERN = /<ol[^>]*>([\\s\\S]*)<\\/ol>/i;\nconst NESTED_LIST_PATTERN = /<[uo]l[^>]*>[\\s\\S]*<\\/[uo]l>/gi;\nconst P_TAG_PATTERN = /<p[^>]*>([\\s\\S]*?)<\\/p>/gi;\nconst P_TAG_SINGLE_PATTERN = /<p[^>]*>([\\s\\S]*?)<\\/p>/i;\nconst HREF_PATTERN = /href=\"([^\"]*)\"/i;\nconst DATA_ID_PATTERN = /data-id=[\"'](\\d+)[\"']/i;\nconst CODE_TAG_PATTERN_SINGLE = /<code[^>]*>([\\s\\S]*?)<\\/code>/i;\nconst TABLE_TAG_PATTERN = /<table[^>]*>([\\s\\S]*?)<\\/table>/i;\nconst THEAD_TAG_PATTERN = /<thead[^>]*>([\\s\\S]*?)<\\/thead>/i;\nconst IMG_TAG_GLOBAL = /<img[^>]+>/gi;\nconst TABLE_ROW_PATTERN = /<tr[^>]*>([\\s\\S]*?)<\\/tr>/gi;\nconst TABLE_CELL_PATTERN = /<(th|td)[^>]*>([\\s\\S]*?)<\\/\\1>/gi;\nconst TBODY_TAG_PATTERN = /<tbody[^>]*>([\\s\\S]*?)<\\/tbody>/i;\nconst CITE_TAG_PATTERN = /<cite[^>]*>([\\s\\S]*?)<\\/cite>/i;\nconst LT_ENTITY_PATTERN = /&lt;/g;\nconst GT_ENTITY_PATTERN = /&gt;/g;\nconst AMP_ENTITY_PATTERN = /&amp;/g;\nconst QUOT_ENTITY_PATTERN = /&quot;/g;\nconst APOS_ENTITY_PATTERN = /&#039;/g;\nconst NBSP_ENTITY_PATTERN = /&nbsp;/g;\n\n/**\n * core/paragraph → block with style \"normal\"\n */\nexport const paragraph: BlockTransformer = (block, _options, context) => {\n\tconst { children, markDefs } = context.parseInlineContent(block.innerHTML);\n\n\t// Skip empty paragraphs\n\tif (children.length === 1 && children[0]?.text === \"\") {\n\t\treturn [];\n\t}\n\n\tconst result: PortableTextTextBlock = {\n\t\t_type: \"block\",\n\t\t_key: context.generateKey(),\n\t\tstyle: \"normal\",\n\t\tchildren,\n\t};\n\n\tif (markDefs.length > 0) {\n\t\tresult.markDefs = markDefs;\n\t}\n\n\treturn [result];\n};\n\n/**\n * core/heading → block with style \"h1\"-\"h6\"\n */\nexport const heading: BlockTransformer = (block, _options, context) => {\n\tconst level = attrNumber(block.attrs, \"level\") ?? 2;\n\tconst { children, markDefs } = context.parseInlineContent(block.innerHTML);\n\n\tconst result: PortableTextTextBlock = {\n\t\t_type: \"block\",\n\t\t_key: context.generateKey(),\n\t\tstyle: toHeadingStyle(level),\n\t\tchildren,\n\t};\n\n\tif (markDefs.length > 0) {\n\t\tresult.markDefs = markDefs;\n\t}\n\n\treturn [result];\n};\n\n/**\n * core/list → blocks with listItem\n *\n * Handles both old format (HTML list) and new format (innerBlocks with list-item)\n */\nexport const list: BlockTransformer = (block, _options, context) => {\n\tconst ordered = block.attrs.ordered === true;\n\tconst listItem = ordered ? \"number\" : \"bullet\";\n\n\t// Check for new format (WordPress 6.x) with core/list-item innerBlocks\n\tif (block.innerBlocks.length > 0) {\n\t\treturn parseListItemBlocks(block.innerBlocks, listItem, 1, context);\n\t}\n\n\t// Old format: HTML content in innerHTML\n\tconst listMatch = block.innerHTML.match(UOL_TAG_PATTERN);\n\tconst listContent = listMatch?.[1] || block.innerHTML;\n\n\treturn parseListItems(listContent, listItem, 1, context);\n};\n\n/**\n * Parse list-item blocks (WordPress 6.x format)\n */\nfunction parseListItemBlocks(\n\tinnerBlocks: GutenbergBlock[],\n\tlistItem: \"bullet\" | \"number\",\n\tlevel: number,\n\tcontext: TransformContext,\n): PortableTextTextBlock[] {\n\tconst blocks: PortableTextTextBlock[] = [];\n\n\tfor (const itemBlock of innerBlocks) {\n\t\tif (itemBlock.blockName !== \"core/list-item\") continue;\n\n\t\t// Get text content from the <li> in innerHTML\n\t\tconst textMatch = itemBlock.innerHTML.match(LI_TAG_PATTERN);\n\t\tconst textContent = textMatch?.[1]?.trim() || \"\";\n\n\t\tif (textContent) {\n\t\t\tconst { children, markDefs } = context.parseInlineContent(textContent);\n\n\t\t\tconst block: PortableTextTextBlock = {\n\t\t\t\t_type: \"block\",\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\tstyle: \"normal\",\n\t\t\t\tlistItem,\n\t\t\t\tlevel,\n\t\t\t\tchildren,\n\t\t\t};\n\n\t\t\tif (markDefs.length > 0) {\n\t\t\t\tblock.markDefs = markDefs;\n\t\t\t}\n\n\t\t\tblocks.push(block);\n\t\t}\n\n\t\t// Handle nested lists in innerBlocks\n\t\tif (itemBlock.innerBlocks.length > 0) {\n\t\t\tfor (const nested of itemBlock.innerBlocks) {\n\t\t\t\tif (nested.blockName === \"core/list\") {\n\t\t\t\t\tconst nestedOrdered = nested.attrs.ordered === true;\n\t\t\t\t\tconst nestedListItem = nestedOrdered ? \"number\" : \"bullet\";\n\t\t\t\t\tblocks.push(\n\t\t\t\t\t\t...parseListItemBlocks(nested.innerBlocks, nestedListItem, level + 1, context),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Parse list items from HTML\n */\nfunction parseListItems(\n\thtml: string,\n\tlistItem: \"bullet\" | \"number\",\n\tlevel: number,\n\tcontext: TransformContext,\n): PortableTextTextBlock[] {\n\tconst blocks: PortableTextTextBlock[] = [];\n\n\t// Match <li> elements - need to handle nested lists carefully\n\t// Find each top-level <li> by tracking tag depth\n\tconst liItems = extractTopLevelListItems(html);\n\n\tfor (const liContent of liItems) {\n\t\t// Check for nested lists\n\t\tconst nestedUl = liContent.match(UL_TAG_PATTERN);\n\t\tconst nestedOl = liContent.match(OL_TAG_PATTERN);\n\n\t\t// Get text content (excluding nested lists)\n\t\tlet textContent = liContent.replace(NESTED_LIST_PATTERN, \"\").trim();\n\n\t\tif (textContent) {\n\t\t\tconst { children, markDefs } = context.parseInlineContent(textContent);\n\n\t\t\tconst block: PortableTextTextBlock = {\n\t\t\t\t_type: \"block\",\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\tstyle: \"normal\",\n\t\t\t\tlistItem,\n\t\t\t\tlevel,\n\t\t\t\tchildren,\n\t\t\t};\n\n\t\t\tif (markDefs.length > 0) {\n\t\t\t\tblock.markDefs = markDefs;\n\t\t\t}\n\n\t\t\tblocks.push(block);\n\t\t}\n\n\t\t// Process nested lists\n\t\tif (nestedUl?.[1]) {\n\t\t\tblocks.push(...parseListItems(nestedUl[1], \"bullet\", level + 1, context));\n\t\t}\n\t\tif (nestedOl?.[1]) {\n\t\t\tblocks.push(...parseListItems(nestedOl[1], \"number\", level + 1, context));\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Extract top-level <li> items from HTML, handling nested lists correctly\n */\nfunction extractTopLevelListItems(html: string): string[] {\n\tconst items: string[] = [];\n\tlet depth = 0;\n\tlet currentItem = \"\";\n\tlet inLi = false;\n\tlet i = 0;\n\n\twhile (i < html.length) {\n\t\t// Check for opening tags\n\t\tif (html.substring(i, i + 3).toLowerCase() === \"<li\") {\n\t\t\t// Find end of tag\n\t\t\tconst tagEnd = html.indexOf(\">\", i);\n\t\t\tif (tagEnd === -1) break;\n\n\t\t\tif (!inLi) {\n\t\t\t\tinLi = true;\n\t\t\t\ti = tagEnd + 1;\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\t// Nested li\n\t\t\t\tcurrentItem += html.substring(i, tagEnd + 1);\n\t\t\t\tdepth++;\n\t\t\t\ti = tagEnd + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Check for closing </li>\n\t\tif (html.substring(i, i + 5).toLowerCase() === \"</li>\") {\n\t\t\tif (depth === 0) {\n\t\t\t\t// End of current top-level item\n\t\t\t\titems.push(currentItem);\n\t\t\t\tcurrentItem = \"\";\n\t\t\t\tinLi = false;\n\t\t\t\ti += 5;\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\t// End of nested item\n\t\t\t\tcurrentItem += \"</li>\";\n\t\t\t\tdepth--;\n\t\t\t\ti += 5;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Check for nested list tags to track depth\n\t\tif (\n\t\t\thtml.substring(i, i + 3).toLowerCase() === \"<ul\" ||\n\t\t\thtml.substring(i, i + 3).toLowerCase() === \"<ol\"\n\t\t) {\n\t\t\tconst tagEnd = html.indexOf(\">\", i);\n\t\t\tif (tagEnd !== -1) {\n\t\t\t\tcurrentItem += html.substring(i, tagEnd + 1);\n\t\t\t\ti = tagEnd + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\thtml.substring(i, i + 5).toLowerCase() === \"</ul>\" ||\n\t\t\thtml.substring(i, i + 5).toLowerCase() === \"</ol>\"\n\t\t) {\n\t\t\tcurrentItem += html.substring(i, i + 5);\n\t\t\ti += 5;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Regular character\n\t\tif (inLi) {\n\t\t\tcurrentItem += html[i];\n\t\t}\n\t\ti++;\n\t}\n\n\t// Handle case where closing </li> is missing\n\tif (currentItem.trim()) {\n\t\titems.push(currentItem);\n\t}\n\n\t// Filter out empty items that may result from whitespace between tags\n\treturn items.filter((item) => item.trim().length > 0);\n}\n\n/**\n * core/quote → block with style \"blockquote\"\n */\nexport const quote: BlockTransformer = (block, _options, context) => {\n\tconst blocks: PortableTextBlock[] = [];\n\n\t// Extract paragraphs from the blockquote\n\tlet match;\n\n\twhile ((match = P_TAG_PATTERN.exec(block.innerHTML)) !== null) {\n\t\tconst content = match[1] || \"\";\n\t\tconst { children, markDefs } = context.parseInlineContent(content);\n\n\t\tconst quoteBlock: PortableTextTextBlock = {\n\t\t\t_type: \"block\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"blockquote\",\n\t\t\tchildren,\n\t\t};\n\n\t\tif (markDefs.length > 0) {\n\t\t\tquoteBlock.markDefs = markDefs;\n\t\t}\n\n\t\tblocks.push(quoteBlock);\n\t}\n\n\t// If no paragraphs found, treat entire content as quote\n\tif (blocks.length === 0) {\n\t\tconst { children, markDefs } = context.parseInlineContent(block.innerHTML);\n\n\t\tconst quoteBlock: PortableTextTextBlock = {\n\t\t\t_type: \"block\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"blockquote\",\n\t\t\tchildren,\n\t\t};\n\n\t\tif (markDefs.length > 0) {\n\t\t\tquoteBlock.markDefs = markDefs;\n\t\t}\n\n\t\tblocks.push(quoteBlock);\n\t}\n\n\t// Handle citation if present\n\tconst citation = attrString(block.attrs, \"citation\");\n\tif (citation) {\n\t\tconst { children, markDefs } = context.parseInlineContent(citation);\n\n\t\tconst citationBlock: PortableTextTextBlock = {\n\t\t\t_type: \"block\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"normal\",\n\t\t\tchildren: [\n\t\t\t\t{\n\t\t\t\t\t_type: \"span\",\n\t\t\t\t\t_key: context.generateKey(),\n\t\t\t\t\ttext: \"— \",\n\t\t\t\t},\n\t\t\t\t...children,\n\t\t\t],\n\t\t};\n\n\t\tif (markDefs.length > 0) {\n\t\t\tcitationBlock.markDefs = markDefs;\n\t\t}\n\n\t\tblocks.push(citationBlock);\n\t}\n\n\treturn blocks;\n};\n\n/**\n * core/image → image block\n */\nexport const image: BlockTransformer = (block, options, context) => {\n\tconst wpId = attrNumber(block.attrs, \"id\");\n\tconst src = attrString(block.attrs, \"url\") ?? extractSrc(block.innerHTML);\n\tconst alt = attrString(block.attrs, \"alt\") ?? extractAlt(block.innerHTML);\n\tconst caption = extractCaption(block.innerHTML);\n\tconst align = attrString(block.attrs, \"align\");\n\n\t// Resolve media ID if we have a map\n\tconst ref = wpId && options.mediaMap?.get(wpId);\n\n\treturn [\n\t\t{\n\t\t\t_type: \"image\",\n\t\t\t_key: context.generateKey(),\n\t\t\tasset: {\n\t\t\t\t_type: \"reference\",\n\t\t\t\t_ref: ref || String(wpId || src || \"\"),\n\t\t\t\turl: src,\n\t\t\t},\n\t\t\talt,\n\t\t\tcaption,\n\t\t\talignment: mapAlignment(align),\n\t\t},\n\t];\n};\n\n/**\n * core/code → code block\n */\nexport const code: BlockTransformer = (block, _options, context) => {\n\t// Extract code from <pre><code>...</code></pre>\n\tconst codeMatch = block.innerHTML.match(CODE_TAG_PATTERN_SINGLE);\n\tconst codeContent = codeMatch?.[1] || block.innerHTML;\n\n\t// Decode HTML entities\n\tconst decoded = decodeHtmlEntities(codeContent);\n\n\treturn [\n\t\t{\n\t\t\t_type: \"code\",\n\t\t\t_key: context.generateKey(),\n\t\t\tcode: decoded,\n\t\t\tlanguage: attrString(block.attrs, \"language\"),\n\t\t},\n\t];\n};\n\n/**\n * core/preformatted → code block (no syntax highlighting)\n */\nexport const preformatted: BlockTransformer = (block, _options, context) => {\n\tconst text = extractText(block.innerHTML);\n\n\treturn [\n\t\t{\n\t\t\t_type: \"code\",\n\t\t\t_key: context.generateKey(),\n\t\t\tcode: text,\n\t\t},\n\t];\n};\n\n/**\n * core/separator / core/spacer → break block\n */\nexport const separator: BlockTransformer = (_block, _options, context) => {\n\treturn [\n\t\t{\n\t\t\t_type: \"break\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"lineBreak\",\n\t\t},\n\t];\n};\n\n/**\n * core/gallery → gallery block\n */\nexport const gallery: BlockTransformer = (block, options, context) => {\n\tconst images: Array<{\n\t\t_type: \"image\";\n\t\t_key: string;\n\t\tasset: { _type: \"reference\"; _ref: string; url?: string };\n\t\talt?: string;\n\t\tcaption?: string;\n\t}> = [];\n\n\t// Extract images from inner blocks or HTML\n\tif (block.innerBlocks.length > 0) {\n\t\tfor (const innerBlock of block.innerBlocks) {\n\t\t\tif (innerBlock.blockName === \"core/image\") {\n\t\t\t\tconst wpId = attrNumber(innerBlock.attrs, \"id\");\n\t\t\t\tconst src = attrString(innerBlock.attrs, \"url\") ?? extractSrc(innerBlock.innerHTML);\n\t\t\t\tconst alt = attrString(innerBlock.attrs, \"alt\") ?? extractAlt(innerBlock.innerHTML);\n\t\t\t\tconst caption = extractCaption(innerBlock.innerHTML);\n\t\t\t\tconst ref = wpId && options.mediaMap?.get(wpId);\n\n\t\t\t\timages.push({\n\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t_key: context.generateKey(),\n\t\t\t\t\tasset: {\n\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t_ref: ref || String(wpId || src || \"\"),\n\t\t\t\t\t\turl: src,\n\t\t\t\t\t},\n\t\t\t\t\talt,\n\t\t\t\t\tcaption,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Parse from HTML (older gallery format)\n\t\tlet match;\n\t\twhile ((match = IMG_TAG_GLOBAL.exec(block.innerHTML)) !== null) {\n\t\t\tconst imgHtml = match[0];\n\t\t\tconst src = extractSrc(imgHtml);\n\t\t\tconst alt = extractAlt(imgHtml);\n\t\t\tconst idMatch = imgHtml.match(DATA_ID_PATTERN);\n\t\t\tconst wpId = idMatch?.[1] ? parseInt(idMatch[1], 10) : undefined;\n\t\t\tconst ref = wpId && options.mediaMap?.get(wpId);\n\n\t\t\timages.push({\n\t\t\t\t_type: \"image\",\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\tasset: {\n\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t_ref: ref || String(wpId || src || \"\"),\n\t\t\t\t\turl: src,\n\t\t\t\t},\n\t\t\t\talt,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn [\n\t\t{\n\t\t\t_type: \"gallery\",\n\t\t\t_key: context.generateKey(),\n\t\t\timages,\n\t\t\tcolumns: attrNumber(block.attrs, \"columns\"),\n\t\t},\n\t];\n};\n\n/**\n * core/columns → columns block\n */\nexport const columns: BlockTransformer = (block, _options, context) => {\n\tconst columnBlocks = block.innerBlocks.map((col) => ({\n\t\t_type: \"column\" as const,\n\t\t_key: context.generateKey(),\n\t\tcontent: context.transformBlocks(col.innerBlocks),\n\t}));\n\n\treturn [\n\t\t{\n\t\t\t_type: \"columns\",\n\t\t\t_key: context.generateKey(),\n\t\t\tcolumns: columnBlocks,\n\t\t},\n\t];\n};\n\n/**\n * core/group → flatten children (no special container)\n */\nexport const group: BlockTransformer = (block, _options, context) => {\n\treturn context.transformBlocks(block.innerBlocks);\n};\n\n/**\n * core/table → table block\n */\nexport const table: BlockTransformer = (block, _options, context) => {\n\tconst _hasFixedLayout = attrBoolean(block.attrs, \"hasFixedLayout\");\n\n\t// Parse the table HTML\n\tconst tableMatch = block.innerHTML.match(TABLE_TAG_PATTERN);\n\tif (!tableMatch) {\n\t\treturn [];\n\t}\n\n\tconst tableContent = tableMatch[1]!;\n\n\t// Check for thead\n\tconst theadMatch = tableContent.match(THEAD_TAG_PATTERN);\n\tconst tbodyMatch = tableContent.match(TBODY_TAG_PATTERN);\n\n\tconst rows: Array<{\n\t\t_type: \"tableRow\";\n\t\t_key: string;\n\t\tcells: Array<{\n\t\t\t_type: \"tableCell\";\n\t\t\t_key: string;\n\t\t\tcontent: import(\"../types.js\").PortableTextSpan[];\n\t\t\tmarkDefs?: import(\"../types.js\").PortableTextMarkDef[];\n\t\t\tisHeader?: boolean;\n\t\t}>;\n\t}> = [];\n\n\t// Parse header rows\n\tif (theadMatch?.[1]) {\n\t\tconst headerRows = parseTableRows(theadMatch[1], context, true);\n\t\trows.push(...headerRows);\n\t}\n\n\t// Parse body rows\n\tif (tbodyMatch?.[1]) {\n\t\tconst bodyRows = parseTableRows(tbodyMatch[1], context, false);\n\t\trows.push(...bodyRows);\n\t} else if (!theadMatch) {\n\t\t// No thead or tbody, parse rows directly\n\t\tconst directRows = parseTableRows(tableContent, context, false);\n\t\trows.push(...directRows);\n\t}\n\n\tif (rows.length === 0) {\n\t\treturn [];\n\t}\n\n\treturn [\n\t\t{\n\t\t\t_type: \"table\" as const,\n\t\t\t_key: context.generateKey(),\n\t\t\trows,\n\t\t\thasHeaderRow: !!theadMatch,\n\t\t},\n\t];\n};\n\n/**\n * Parse table rows from HTML\n */\nfunction parseTableRows(\n\thtml: string,\n\tcontext: import(\"../types.js\").TransformContext,\n\tisHeader: boolean,\n): Array<{\n\t_type: \"tableRow\";\n\t_key: string;\n\tcells: Array<{\n\t\t_type: \"tableCell\";\n\t\t_key: string;\n\t\tcontent: import(\"../types.js\").PortableTextSpan[];\n\t\tmarkDefs?: import(\"../types.js\").PortableTextMarkDef[];\n\t\tisHeader?: boolean;\n\t}>;\n}> {\n\tconst rows: Array<{\n\t\t_type: \"tableRow\";\n\t\t_key: string;\n\t\tcells: Array<{\n\t\t\t_type: \"tableCell\";\n\t\t\t_key: string;\n\t\t\tcontent: import(\"../types.js\").PortableTextSpan[];\n\t\t\tmarkDefs?: import(\"../types.js\").PortableTextMarkDef[];\n\t\t\tisHeader?: boolean;\n\t\t}>;\n\t}> = [];\n\n\tlet rowMatch;\n\n\twhile ((rowMatch = TABLE_ROW_PATTERN.exec(html)) !== null) {\n\t\tconst rowContent = rowMatch[1]!;\n\t\tconst cells: Array<{\n\t\t\t_type: \"tableCell\";\n\t\t\t_key: string;\n\t\t\tcontent: import(\"../types.js\").PortableTextSpan[];\n\t\t\tmarkDefs?: import(\"../types.js\").PortableTextMarkDef[];\n\t\t\tisHeader?: boolean;\n\t\t}> = [];\n\n\t\t// Match both th and td cells\n\t\tlet cellMatch;\n\n\t\twhile ((cellMatch = TABLE_CELL_PATTERN.exec(rowContent)) !== null) {\n\t\t\tconst isHeaderCell = cellMatch[1]!.toLowerCase() === \"th\" || isHeader;\n\t\t\tconst cellContent = cellMatch[2]!;\n\n\t\t\tconst { children, markDefs } = context.parseInlineContent(cellContent);\n\n\t\t\tcells.push({\n\t\t\t\t_type: \"tableCell\" as const,\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\tcontent: children,\n\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\tisHeader: isHeaderCell || undefined,\n\t\t\t});\n\t\t}\n\n\t\tif (cells.length > 0) {\n\t\t\trows.push({\n\t\t\t\t_type: \"tableRow\" as const,\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\tcells,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn rows;\n}\n\n/**\n * Convert a heading level number to a PortableTextTextBlock style\n */\nfunction toHeadingStyle(level: number): PortableTextTextBlock[\"style\"] {\n\tswitch (level) {\n\t\tcase 1:\n\t\t\treturn \"h1\";\n\t\tcase 2:\n\t\t\treturn \"h2\";\n\t\tcase 3:\n\t\t\treturn \"h3\";\n\t\tcase 4:\n\t\t\treturn \"h4\";\n\t\tcase 5:\n\t\t\treturn \"h5\";\n\t\tcase 6:\n\t\t\treturn \"h6\";\n\t\tdefault:\n\t\t\treturn \"h2\";\n\t}\n}\n\n/**\n * Map WordPress alignment to Portable Text alignment\n */\nfunction mapAlignment(\n\talign: string | undefined,\n): \"left\" | \"center\" | \"right\" | \"wide\" | \"full\" | undefined {\n\tswitch (align) {\n\t\tcase \"left\":\n\t\tcase \"center\":\n\t\tcase \"right\":\n\t\tcase \"wide\":\n\t\tcase \"full\":\n\t\t\treturn align;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Decode HTML entities\n */\nfunction decodeHtmlEntities(html: string): string {\n\treturn html\n\t\t.replace(LT_ENTITY_PATTERN, \"<\")\n\t\t.replace(GT_ENTITY_PATTERN, \">\")\n\t\t.replace(AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(QUOT_ENTITY_PATTERN, '\"')\n\t\t.replace(APOS_ENTITY_PATTERN, \"'\")\n\t\t.replace(NBSP_ENTITY_PATTERN, \" \");\n}\n\n/**\n * core/button → button block\n */\nexport const button: BlockTransformer = (block, _options, context) => {\n\tconst url = sanitizeHref(attrString(block.attrs, \"url\"));\n\tconst text = extractText(block.innerHTML).trim() || \"Button\";\n\n\t// Detect button style from className\n\tlet style: \"default\" | \"outline\" | \"fill\" = \"default\";\n\tconst className = attrString(block.attrs, \"className\");\n\tif (className?.includes(\"is-style-outline\")) {\n\t\tstyle = \"outline\";\n\t} else if (className?.includes(\"is-style-fill\")) {\n\t\tstyle = \"fill\";\n\t}\n\n\treturn [\n\t\t{\n\t\t\t_type: \"button\",\n\t\t\t_key: context.generateKey(),\n\t\t\ttext,\n\t\t\turl,\n\t\t\tstyle,\n\t\t},\n\t];\n};\n\n/**\n * core/buttons → buttons container block\n */\nexport const buttons: BlockTransformer = (block, _options, context) => {\n\tconst buttonBlocks: Array<{\n\t\t_type: \"button\";\n\t\t_key: string;\n\t\ttext: string;\n\t\turl?: string;\n\t\tstyle?: \"default\" | \"outline\" | \"fill\";\n\t}> = [];\n\n\tfor (const innerBlock of block.innerBlocks) {\n\t\tif (innerBlock.blockName === \"core/button\") {\n\t\t\tconst url = attrString(innerBlock.attrs, \"url\");\n\t\t\tconst text = extractText(innerBlock.innerHTML).trim() || \"Button\";\n\n\t\t\tlet style: \"default\" | \"outline\" | \"fill\" = \"default\";\n\t\t\tconst className = attrString(innerBlock.attrs, \"className\");\n\t\t\tif (className?.includes(\"is-style-outline\")) {\n\t\t\t\tstyle = \"outline\";\n\t\t\t} else if (className?.includes(\"is-style-fill\")) {\n\t\t\t\tstyle = \"fill\";\n\t\t\t}\n\n\t\t\tbuttonBlocks.push({\n\t\t\t\t_type: \"button\",\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\ttext,\n\t\t\t\turl,\n\t\t\t\tstyle,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Detect layout from attrs\n\tconst layoutObj = attrObject(block.attrs, \"layout\");\n\tconst layout =\n\t\tlayoutObj && typeof layoutObj[\"type\"] === \"string\" && layoutObj[\"type\"] === \"flex\"\n\t\t\t? \"horizontal\"\n\t\t\t: \"vertical\";\n\n\treturn [\n\t\t{\n\t\t\t_type: \"buttons\",\n\t\t\t_key: context.generateKey(),\n\t\t\tbuttons: buttonBlocks,\n\t\t\tlayout: layout,\n\t\t},\n\t];\n};\n\n/**\n * core/cover → cover block\n */\nexport const cover: BlockTransformer = (block, _options, context) => {\n\tconst url = attrString(block.attrs, \"url\");\n\tconst overlayColor = attrString(block.attrs, \"overlayColor\");\n\tconst customOverlayColor = attrString(block.attrs, \"customOverlayColor\");\n\tconst dimRatio = attrNumber(block.attrs, \"dimRatio\");\n\tconst minHeight = attrString(block.attrs, \"minHeight\");\n\tconst minHeightUnit = attrString(block.attrs, \"minHeightUnit\");\n\tconst contentPosition = attrString(block.attrs, \"contentPosition\");\n\n\t// Transform inner blocks for content\n\tconst content = context.transformBlocks(block.innerBlocks);\n\n\t// Determine alignment from content position\n\tlet alignment: \"left\" | \"center\" | \"right\" | undefined;\n\tif (contentPosition?.includes(\"left\")) alignment = \"left\";\n\telse if (contentPosition?.includes(\"right\")) alignment = \"right\";\n\telse if (contentPosition?.includes(\"center\")) alignment = \"center\";\n\n\t// Build min height string\n\tlet minHeightStr: string | undefined;\n\tif (minHeight) {\n\t\tminHeightStr = minHeightUnit ? `${minHeight}${minHeightUnit}` : `${minHeight}px`;\n\t}\n\n\treturn [\n\t\t{\n\t\t\t_type: \"cover\",\n\t\t\t_key: context.generateKey(),\n\t\t\tbackgroundImage: url,\n\t\t\toverlayColor: customOverlayColor || overlayColor,\n\t\t\toverlayOpacity: dimRatio !== undefined ? dimRatio / 100 : undefined,\n\t\t\tcontent,\n\t\t\tminHeight: minHeightStr,\n\t\t\talignment,\n\t\t},\n\t];\n};\n\n/**\n * core/file → file block\n */\nexport const file: BlockTransformer = (block, _options, context) => {\n\tconst href = sanitizeHref(attrString(block.attrs, \"href\"));\n\tconst fileName = attrString(block.attrs, \"fileName\");\n\tconst showDownloadButton = attrBoolean(block.attrs, \"showDownloadButton\");\n\n\t// Try to extract href from HTML if not in attrs\n\tlet url = href;\n\tif (!url) {\n\t\tconst hrefMatch = block.innerHTML.match(HREF_PATTERN);\n\t\turl = sanitizeHref(hrefMatch?.[1]);\n\t}\n\n\t// Try to extract filename from HTML if not in attrs\n\tlet filename = fileName;\n\tif (!filename && url) {\n\t\tfilename = url.split(\"/\").pop()?.split(\"?\")[0];\n\t}\n\n\treturn [\n\t\t{\n\t\t\t_type: \"file\",\n\t\t\t_key: context.generateKey(),\n\t\t\turl: url || \"\",\n\t\t\tfilename,\n\t\t\tshowDownloadButton: showDownloadButton !== false,\n\t\t},\n\t];\n};\n\n/**\n * core/pullquote → pullquote block\n */\nexport const pullquote: BlockTransformer = (block, _options, context) => {\n\t// Extract text from blockquote > p\n\tconst pMatch = block.innerHTML.match(P_TAG_SINGLE_PATTERN);\n\tconst text = pMatch ? extractText(pMatch[1]!) : extractText(block.innerHTML);\n\n\t// Extract citation\n\tconst citeMatch = block.innerHTML.match(CITE_TAG_PATTERN);\n\tconst citation = citeMatch ? extractText(citeMatch[1]!) : attrString(block.attrs, \"citation\");\n\n\treturn [\n\t\t{\n\t\t\t_type: \"pullquote\",\n\t\t\t_key: context.generateKey(),\n\t\t\ttext: text.trim(),\n\t\t\tcitation: citation?.trim(),\n\t\t},\n\t];\n};\n\n/**\n * core/html → htmlBlock (pass through)\n */\nexport const html: BlockTransformer = (block, _options, context) => {\n\treturn [\n\t\t{\n\t\t\t_type: \"htmlBlock\",\n\t\t\t_key: context.generateKey(),\n\t\t\thtml: block.innerHTML.trim(),\n\t\t\toriginalBlockName: \"core/html\",\n\t\t},\n\t];\n};\n\n/**\n * core/verse → code block (preserves whitespace like preformatted)\n */\nexport const verse: BlockTransformer = (block, _options, context) => {\n\tconst text = extractText(block.innerHTML);\n\n\treturn [\n\t\t{\n\t\t\t_type: \"code\",\n\t\t\t_key: context.generateKey(),\n\t\t\tcode: text,\n\t\t\tlanguage: \"text\", // Mark as plain text\n\t\t},\n\t];\n};\n\n/**\n * core/more → break block with \"readMore\" style\n */\nexport const more: BlockTransformer = (_block, _options, context) => {\n\treturn [\n\t\t{\n\t\t\t_type: \"break\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"lineBreak\", // Could be \"readMore\" if we add that type\n\t\t},\n\t];\n};\n\n/**\n * core/nextpage → break block with page break indicator\n */\nexport const nextpage: BlockTransformer = (_block, _options, context) => {\n\treturn [\n\t\t{\n\t\t\t_type: \"break\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"lineBreak\", // Could be \"pageBreak\" if we add that type\n\t\t},\n\t];\n};\n\n/**\n * core/shortcode → htmlBlock (preserve for manual handling)\n */\nexport const shortcode: BlockTransformer = (block, _options, context) => {\n\treturn [\n\t\t{\n\t\t\t_type: \"htmlBlock\",\n\t\t\t_key: context.generateKey(),\n\t\t\thtml: block.innerHTML.trim(),\n\t\t\toriginalBlockName: \"core/shortcode\",\n\t\t},\n\t];\n};\n\n/**\n * core/media-text → columns block with 2 columns\n */\nexport const mediaText: BlockTransformer = (block, _options, context) => {\n\tconst mediaId = attrNumber(block.attrs, \"mediaId\");\n\tconst mediaUrl = attrString(block.attrs, \"mediaUrl\");\n\tconst mediaType = attrString(block.attrs, \"mediaType\");\n\tconst mediaPosition = attrString(block.attrs, \"mediaPosition\");\n\tconst mediaAlt = attrString(block.attrs, \"mediaAlt\");\n\n\t// Create media column\n\tconst mediaBlock: PortableTextBlock[] =\n\t\tmediaType === \"video\"\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\t_type: \"embed\",\n\t\t\t\t\t\t_key: context.generateKey(),\n\t\t\t\t\t\turl: mediaUrl || \"\",\n\t\t\t\t\t\tprovider: \"video\",\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: [\n\t\t\t\t\t{\n\t\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t\t_key: context.generateKey(),\n\t\t\t\t\t\tasset: {\n\t\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t\t_ref: String(mediaId || mediaUrl || \"\"),\n\t\t\t\t\t\t\turl: mediaUrl,\n\t\t\t\t\t\t},\n\t\t\t\t\t\talt: mediaAlt,\n\t\t\t\t\t},\n\t\t\t\t];\n\n\t// Transform content blocks\n\tconst contentBlocks = context.transformBlocks(block.innerBlocks);\n\n\t// Order based on media position\n\tconst mediaTextColumns =\n\t\tmediaPosition === \"right\"\n\t\t\t? [\n\t\t\t\t\t{ _type: \"column\" as const, _key: context.generateKey(), content: contentBlocks },\n\t\t\t\t\t{ _type: \"column\" as const, _key: context.generateKey(), content: mediaBlock },\n\t\t\t\t]\n\t\t\t: [\n\t\t\t\t\t{ _type: \"column\" as const, _key: context.generateKey(), content: mediaBlock },\n\t\t\t\t\t{ _type: \"column\" as const, _key: context.generateKey(), content: contentBlocks },\n\t\t\t\t];\n\n\treturn [\n\t\t{\n\t\t\t_type: \"columns\",\n\t\t\t_key: context.generateKey(),\n\t\t\tcolumns: mediaTextColumns,\n\t\t},\n\t];\n};\n","/**\n * Transformers for WordPress embed blocks\n */\n\nimport type { BlockTransformer } from \"../types.js\";\nimport { attrString } from \"../types.js\";\n\n// Regex patterns for embed parsing\nconst IFRAME_SRC_PATTERN = /<iframe[^>]+src=[\"']([^\"']+)[\"']/i;\nconst VIDEO_SRC_PATTERN = /<video[^>]+src=[\"']([^\"']+)[\"']/i;\nconst VIDEO_SOURCE_PATTERN = /<source[^>]+src=[\"']([^\"']+)[\"']/i;\nconst AUDIO_SRC_PATTERN = /<audio[^>]+src=[\"']([^\"']+)[\"']/i;\nconst AUDIO_SOURCE_PATTERN = /<source[^>]+src=[\"']([^\"']+)[\"']/i;\n\n/**\n * core/embed and variants → embed block\n */\nexport const embed: BlockTransformer = (block, _options, context) => {\n\tconst url = attrString(block.attrs, \"url\");\n\tconst providerSlug = attrString(block.attrs, \"providerNameSlug\");\n\n\t// Extract iframe src if present\n\tconst iframeMatch = block.innerHTML.match(IFRAME_SRC_PATTERN);\n\tconst iframeSrc = iframeMatch?.[1];\n\n\treturn [\n\t\t{\n\t\t\t_type: \"embed\",\n\t\t\t_key: context.generateKey(),\n\t\t\turl: url || iframeSrc || \"\",\n\t\t\tprovider: providerSlug || detectProvider(url || iframeSrc || \"\"),\n\t\t\thtml: block.innerHTML.trim() || undefined,\n\t\t},\n\t];\n};\n\n/**\n * core-embed/youtube → embed block\n */\nexport const youtube: BlockTransformer = (block, options, context) => {\n\treturn embed(block, options, context);\n};\n\n/**\n * core-embed/twitter → embed block\n */\nexport const twitter: BlockTransformer = (block, options, context) => {\n\treturn embed(block, options, context);\n};\n\n/**\n * core-embed/vimeo → embed block\n */\nexport const vimeo: BlockTransformer = (block, options, context) => {\n\treturn embed(block, options, context);\n};\n\n/**\n * core/video → embed block (self-hosted video)\n */\nexport const video: BlockTransformer = (block, _options, context) => {\n\tconst src = attrString(block.attrs, \"src\");\n\n\t// Extract from video tag if not in attrs\n\tconst videoMatch = block.innerHTML.match(VIDEO_SRC_PATTERN);\n\tconst sourceMatch = block.innerHTML.match(VIDEO_SOURCE_PATTERN);\n\tconst videoSrc = src || videoMatch?.[1] || sourceMatch?.[1];\n\n\treturn [\n\t\t{\n\t\t\t_type: \"embed\",\n\t\t\t_key: context.generateKey(),\n\t\t\turl: videoSrc || \"\",\n\t\t\tprovider: \"video\",\n\t\t\thtml: block.innerHTML.trim() || undefined,\n\t\t},\n\t];\n};\n\n/**\n * core/audio → embed block (self-hosted audio)\n */\nexport const audio: BlockTransformer = (block, _options, context) => {\n\tconst src = attrString(block.attrs, \"src\");\n\n\t// Extract from audio tag if not in attrs\n\tconst audioMatch = block.innerHTML.match(AUDIO_SRC_PATTERN);\n\tconst sourceMatch = block.innerHTML.match(AUDIO_SOURCE_PATTERN);\n\tconst audioSrc = src || audioMatch?.[1] || sourceMatch?.[1];\n\n\treturn [\n\t\t{\n\t\t\t_type: \"embed\",\n\t\t\t_key: context.generateKey(),\n\t\t\turl: audioSrc || \"\",\n\t\t\tprovider: \"audio\",\n\t\t\thtml: block.innerHTML.trim() || undefined,\n\t\t},\n\t];\n};\n\n/**\n * Detect embed provider from URL\n */\nfunction detectProvider(url: string): string | undefined {\n\tif (!url) return undefined;\n\n\tconst urlLower = url.toLowerCase();\n\n\tif (urlLower.includes(\"youtube.com\") || urlLower.includes(\"youtu.be\")) {\n\t\treturn \"youtube\";\n\t}\n\tif (urlLower.includes(\"vimeo.com\")) {\n\t\treturn \"vimeo\";\n\t}\n\tif (urlLower.includes(\"twitter.com\") || urlLower.includes(\"x.com\")) {\n\t\treturn \"twitter\";\n\t}\n\tif (urlLower.includes(\"instagram.com\")) {\n\t\treturn \"instagram\";\n\t}\n\tif (urlLower.includes(\"facebook.com\")) {\n\t\treturn \"facebook\";\n\t}\n\tif (urlLower.includes(\"tiktok.com\")) {\n\t\treturn \"tiktok\";\n\t}\n\tif (urlLower.includes(\"spotify.com\")) {\n\t\treturn \"spotify\";\n\t}\n\tif (urlLower.includes(\"soundcloud.com\")) {\n\t\treturn \"soundcloud\";\n\t}\n\tif (urlLower.includes(\"codepen.io\")) {\n\t\treturn \"codepen\";\n\t}\n\tif (urlLower.includes(\"gist.github.com\")) {\n\t\treturn \"gist\";\n\t}\n\n\treturn undefined;\n}\n","/**\n * Block transformers registry\n */\n\nimport type { BlockTransformer, PortableTextBlock } from \"../types.js\";\nimport * as core from \"./core.js\";\nimport * as embed from \"./embed.js\";\n\n/**\n * Default block transformers for core WordPress blocks\n */\nexport const defaultTransformers: Record<string, BlockTransformer> = {\n\t// Text blocks\n\t\"core/paragraph\": core.paragraph,\n\t\"core/heading\": core.heading,\n\t\"core/list\": core.list,\n\t\"core/quote\": core.quote,\n\t\"core/code\": core.code,\n\t\"core/preformatted\": core.preformatted,\n\t\"core/pullquote\": core.pullquote,\n\t\"core/verse\": core.verse,\n\n\t// Media blocks\n\t\"core/image\": core.image,\n\t\"core/gallery\": core.gallery,\n\t\"core/file\": core.file,\n\t\"core/media-text\": core.mediaText,\n\t\"core/cover\": core.cover,\n\n\t// Layout blocks\n\t\"core/columns\": core.columns,\n\t\"core/group\": core.group,\n\t\"core/separator\": core.separator,\n\t\"core/spacer\": core.separator,\n\t\"core/table\": core.table,\n\t\"core/buttons\": core.buttons,\n\t\"core/button\": core.button,\n\n\t// Structural blocks\n\t\"core/more\": core.more,\n\t\"core/nextpage\": core.nextpage,\n\n\t// Pass-through blocks (preserve as HTML)\n\t\"core/html\": core.html,\n\t\"core/shortcode\": core.shortcode,\n\n\t// Embed blocks\n\t\"core/embed\": embed.embed,\n\t\"core/video\": embed.video,\n\t\"core/audio\": embed.audio,\n\n\t// Legacy embed block names (WP < 5.6)\n\t\"core-embed/youtube\": embed.youtube,\n\t\"core-embed/twitter\": embed.twitter,\n\t\"core-embed/vimeo\": embed.vimeo,\n\t\"core-embed/facebook\": embed.embed,\n\t\"core-embed/instagram\": embed.embed,\n\t\"core-embed/soundcloud\": embed.embed,\n\t\"core-embed/spotify\": embed.embed,\n};\n\n/**\n * Fallback transformer for unknown blocks\n * Stores the original HTML for manual review\n */\nexport const fallbackTransformer: BlockTransformer = (\n\tblock,\n\t_options,\n\tcontext,\n): PortableTextBlock[] => {\n\t// Skip completely empty blocks\n\tif (!block.innerHTML.trim() && block.innerBlocks.length === 0) {\n\t\treturn [];\n\t}\n\n\t// If it has inner blocks, try to transform those\n\tif (block.innerBlocks.length > 0) {\n\t\treturn context.transformBlocks(block.innerBlocks);\n\t}\n\n\t// Store as HTML fallback\n\treturn [\n\t\t{\n\t\t\t_type: \"htmlBlock\",\n\t\t\t_key: context.generateKey(),\n\t\t\thtml: block.innerHTML,\n\t\t\toriginalBlockName: block.blockName,\n\t\t\toriginalAttrs: Object.keys(block.attrs).length > 0 ? block.attrs : undefined,\n\t\t},\n\t];\n};\n\n/**\n * Get transformer for a block\n */\nexport function getTransformer(\n\tblockName: string | null,\n\tcustomTransformers?: Record<string, BlockTransformer>,\n): BlockTransformer {\n\tif (!blockName) {\n\t\treturn fallbackTransformer;\n\t}\n\n\t// Check custom transformers first\n\tif (customTransformers?.[blockName]) {\n\t\treturn customTransformers[blockName];\n\t}\n\n\t// Check default transformers\n\tif (defaultTransformers[blockName]) {\n\t\treturn defaultTransformers[blockName];\n\t}\n\n\treturn fallbackTransformer;\n}\n","/**\n * Gutenberg to Portable Text Converter\n *\n * Converts WordPress Gutenberg block content to Portable Text format.\n * Uses @wordpress/block-serialization-default-parser to parse the hybrid\n * HTML+JSON format that WordPress uses.\n */\n\nimport { parse } from \"@wordpress/block-serialization-default-parser\";\n\nimport { parseInlineContent } from \"./inline.js\";\nimport { getTransformer, defaultTransformers, fallbackTransformer } from \"./transformers/index.js\";\nimport type {\n\tGutenbergBlock,\n\tPortableTextBlock,\n\tConvertOptions,\n\tTransformContext,\n} from \"./types.js\";\n\n// Regex patterns for HTML parsing and conversion\nconst BLOCK_ELEMENT_PATTERN =\n\t/<(p|h[1-6]|blockquote|pre|ul|ol|figure|div|hr)[^>]*>([\\s\\S]*?)<\\/\\1>|<(hr|br)\\s*\\/?>|<img\\s+[^>]+\\/?>/gu;\nconst LINKED_IMAGE_PATTERN = /<a\\s+[^>]*href=[\"']([^\"']+)[\"'][^>]*>\\s*<img\\s+([^>]+)\\/?>\\s*<\\/a>/gu;\nconst STANDALONE_IMAGE_PATTERN = /<img\\s+[^>]+\\/?>/gu;\nconst IMG_TAG_PATTERN = /<img[^>]+>/i;\nconst SRC_ATTR_PATTERN = /src=[\"']([^\"']+)[\"']/i;\nconst ALT_ATTR_PATTERN = /alt=[\"']([^\"']*)[\"']/i;\nconst LIST_ITEM_PATTERN = /<li[^>]*>([\\s\\S]*?)<\\/li>/gu;\nconst CODE_TAG_PATTERN = /<code[^>]*>([\\s\\S]*?)<\\/code>/i;\nconst HTML_TAG_PATTERN = /<[^>]+>/g;\nconst FIGCAPTION_TAG_PATTERN = /<figcaption[^>]*>([\\s\\S]*?)<\\/figcaption>/i;\nconst AMP_ENTITY_PATTERN = /&amp;/g;\nconst LESS_THAN_ENTITY_PATTERN = /&lt;/g;\nconst GREATER_THAN_ENTITY_PATTERN = /&gt;/g;\nconst QUOTE_ENTITY_PATTERN = /&quot;/g;\nconst APOS_ENTITY_PATTERN = /&#039;/g;\nconst NUMERIC_AMP_ENTITY_PATTERN = /&#0?38;/g;\nconst HEX_AMP_ENTITY_PATTERN = /&#x26;/gi;\nconst NBSP_ENTITY_PATTERN = /&nbsp;/g;\n\n// Re-export types\nexport type {\n\tGutenbergBlock,\n\tPortableTextBlock,\n\tPortableTextTextBlock,\n\tPortableTextImageBlock,\n\tPortableTextCodeBlock,\n\tPortableTextEmbedBlock,\n\tPortableTextGalleryBlock,\n\tPortableTextColumnsBlock,\n\tPortableTextBreakBlock,\n\tPortableTextHtmlBlock,\n\tPortableTextButtonBlock,\n\tPortableTextButtonsBlock,\n\tPortableTextCoverBlock,\n\tPortableTextFileBlock,\n\tPortableTextPullquoteBlock,\n\tPortableTextSpan,\n\tPortableTextMarkDef,\n\tConvertOptions,\n\tBlockTransformer,\n\tTransformContext,\n} from \"./types.js\";\n\n// Re-export transformers for customization\nexport { defaultTransformers, fallbackTransformer } from \"./transformers/index.js\";\nexport * as coreTransformers from \"./transformers/core.js\";\nexport * as embedTransformers from \"./transformers/embed.js\";\n\n// Re-export inline utilities\nexport {\n\tparseInlineContent,\n\textractText,\n\textractAlt,\n\textractCaption,\n\textractSrc,\n} from \"./inline.js\";\n\n/**\n * Default key generator\n */\nfunction createKeyGenerator(): () => string {\n\tlet counter = 0;\n\treturn () => {\n\t\tcounter++;\n\t\treturn `key-${counter}-${Math.random().toString(36).substring(2, 7)}`;\n\t};\n}\n\n/**\n * Normalize parsed blocks from the WP parser into our GutenbergBlock type.\n * The WP parser returns `attrs: Record<string, any> | null`, so we normalize\n * null attrs to empty objects and recursively process innerBlocks.\n */\nfunction normalizeBlocks(blocks: ReturnType<typeof parse>): GutenbergBlock[] {\n\treturn blocks.map(\n\t\t(block): GutenbergBlock => ({\n\t\t\tblockName: block.blockName,\n\t\t\tattrs: (block.attrs ?? {}) satisfies Record<string, unknown>,\n\t\t\tinnerHTML: block.innerHTML,\n\t\t\tinnerBlocks: normalizeBlocks(block.innerBlocks),\n\t\t\tinnerContent: block.innerContent,\n\t\t}),\n\t);\n}\n\n/**\n * Convert WordPress Gutenberg content to Portable Text\n *\n * @param content - WordPress post content (HTML with Gutenberg block comments)\n * @param options - Conversion options\n * @returns Array of Portable Text blocks\n *\n * @example\n * ```ts\n * const portableText = gutenbergToPortableText(`\n * <!-- wp:paragraph -->\n * <p>Hello <strong>world</strong>!</p>\n * <!-- /wp:paragraph -->\n * `);\n * // → [{ _type: \"block\", style: \"normal\", children: [...] }]\n * ```\n */\nexport function gutenbergToPortableText(\n\tcontent: string,\n\toptions: ConvertOptions = {},\n): PortableTextBlock[] {\n\t// Handle empty content\n\tif (!content || !content.trim()) {\n\t\treturn [];\n\t}\n\n\t// Check if content has Gutenberg blocks\n\tconst hasBlocks = content.includes(\"<!-- wp:\");\n\n\tif (!hasBlocks) {\n\t\t// Classic editor content - treat as HTML\n\t\treturn htmlToPortableText(content, options);\n\t}\n\n\t// Parse Gutenberg blocks\n\tconst blocks = normalizeBlocks(parse(content));\n\n\t// Create key generator\n\tconst generateKey = options.keyGenerator || createKeyGenerator();\n\n\t// Create transform context\n\tconst context = createTransformContext(options, generateKey);\n\n\t// Transform blocks\n\treturn blocks.flatMap((block) => transformBlock(block, options, context));\n}\n\n/**\n * Convert plain HTML (classic editor) to Portable Text\n */\nexport function htmlToPortableText(\n\thtml: string,\n\toptions: ConvertOptions = {},\n): PortableTextBlock[] {\n\tconst generateKey = options.keyGenerator || createKeyGenerator();\n\tconst blocks: PortableTextBlock[] = [];\n\n\t// Split on block-level elements (including standalone img tags)\n\tlet lastIndex = 0;\n\tlet match;\n\n\twhile ((match = BLOCK_ELEMENT_PATTERN.exec(html)) !== null) {\n\t\tconst fullMatch = match[0];\n\t\tconst tag = (match[1] || match[3] || \"\").toLowerCase();\n\t\tconst content = match[2] || \"\";\n\n\t\t// Handle text between matches\n\t\tconst between = html.slice(lastIndex, match.index).trim();\n\t\tif (between) {\n\t\t\tconst { children, markDefs } = parseInlineContent(between, generateKey);\n\t\t\tif (children.some((c) => c.text.trim())) {\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tstyle: \"normal\",\n\t\t\t\t\tchildren,\n\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tlastIndex = match.index + match[0].length;\n\n\t\t// Check for standalone <img> tag (not wrapped in figure/p)\n\t\tif (fullMatch.toLowerCase().startsWith(\"<img\")) {\n\t\t\tconst srcMatch = fullMatch.match(SRC_ATTR_PATTERN);\n\t\t\tconst altMatch = fullMatch.match(ALT_ATTR_PATTERN);\n\t\t\tif (srcMatch?.[1]) {\n\t\t\t\tconst imgUrl = decodeUrlEntities(srcMatch[1]);\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tasset: {\n\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t_ref: imgUrl,\n\t\t\t\t\t\turl: imgUrl,\n\t\t\t\t\t},\n\t\t\t\t\talt: altMatch?.[1],\n\t\t\t\t});\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Transform based on tag\n\t\tswitch (tag) {\n\t\t\tcase \"p\":\n\t\t\tcase \"div\": {\n\t\t\t\t// Extract any images first (including those wrapped in <a> tags)\n\t\t\t\t// Match: <a...><img...></a> or standalone <img...>\n\t\t\t\t// Track positions of linked images so we don't double-process\n\t\t\t\tconst linkedImgPositions: Array<{ start: number; end: number }> = [];\n\n\t\t\t\t// First extract linked images\n\t\t\t\tlet linkedMatch;\n\t\t\t\twhile ((linkedMatch = LINKED_IMAGE_PATTERN.exec(content)) !== null) {\n\t\t\t\t\tconst linkUrl = decodeUrlEntities(linkedMatch[1]!);\n\t\t\t\t\tconst imgAttrs = linkedMatch[2]!;\n\t\t\t\t\tconst srcMatch = imgAttrs.match(SRC_ATTR_PATTERN);\n\t\t\t\t\tconst altMatch = imgAttrs.match(ALT_ATTR_PATTERN);\n\t\t\t\t\tif (srcMatch?.[1]) {\n\t\t\t\t\t\tconst imgUrl = decodeUrlEntities(srcMatch[1]);\n\t\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\tasset: {\n\t\t\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t\t\t_ref: imgUrl,\n\t\t\t\t\t\t\t\turl: imgUrl,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\talt: altMatch?.[1],\n\t\t\t\t\t\t\tlink: linkUrl,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tlinkedImgPositions.push({\n\t\t\t\t\t\tstart: linkedMatch.index,\n\t\t\t\t\t\tend: linkedMatch.index + linkedMatch[0].length,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Then extract standalone images (not inside <a> tags)\n\t\t\t\tlet imgMatch;\n\t\t\t\twhile ((imgMatch = STANDALONE_IMAGE_PATTERN.exec(content)) !== null) {\n\t\t\t\t\t// Skip if this image is inside a linked image we already processed\n\t\t\t\t\tconst isLinked = linkedImgPositions.some(\n\t\t\t\t\t\t(pos) => imgMatch!.index >= pos.start && imgMatch!.index < pos.end,\n\t\t\t\t\t);\n\t\t\t\t\tif (isLinked) continue;\n\n\t\t\t\t\tconst srcMatch = imgMatch[0].match(SRC_ATTR_PATTERN);\n\t\t\t\t\tconst altMatch = imgMatch[0].match(ALT_ATTR_PATTERN);\n\t\t\t\t\tif (srcMatch?.[1]) {\n\t\t\t\t\t\tconst imgUrl = decodeUrlEntities(srcMatch[1]);\n\t\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\tasset: {\n\t\t\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t\t\t_ref: imgUrl,\n\t\t\t\t\t\t\t\turl: imgUrl,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\talt: altMatch?.[1],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Then handle the text content (with images and image links stripped)\n\t\t\t\tlet textContent = content\n\t\t\t\t\t.replace(LINKED_IMAGE_PATTERN, \"\") // Remove linked images\n\t\t\t\t\t.replace(STANDALONE_IMAGE_PATTERN, \"\") // Remove standalone images\n\t\t\t\t\t.trim();\n\t\t\t\tif (textContent) {\n\t\t\t\t\tconst { children, markDefs } = parseInlineContent(textContent, generateKey);\n\t\t\t\t\tif (children.some((c) => c.text.trim())) {\n\t\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\tstyle: \"normal\",\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"h1\":\n\t\t\tcase \"h2\":\n\t\t\tcase \"h3\":\n\t\t\tcase \"h4\":\n\t\t\tcase \"h5\":\n\t\t\tcase \"h6\": {\n\t\t\t\tconst { children, markDefs } = parseInlineContent(content, generateKey);\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tstyle: tag,\n\t\t\t\t\tchildren,\n\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"blockquote\": {\n\t\t\t\tconst { children, markDefs } = parseInlineContent(content, generateKey);\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tstyle: \"blockquote\",\n\t\t\t\t\tchildren,\n\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"pre\": {\n\t\t\t\t// Extract code content\n\t\t\t\tconst codeMatch = content.match(CODE_TAG_PATTERN);\n\t\t\t\tconst code = codeMatch?.[1] || content;\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"code\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tcode: decodeHtmlEntities(code),\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"ul\":\n\t\t\tcase \"ol\": {\n\t\t\t\tconst listItem = tag === \"ol\" ? \"number\" : \"bullet\";\n\t\t\t\tlet liMatch;\n\t\t\t\twhile ((liMatch = LIST_ITEM_PATTERN.exec(content)) !== null) {\n\t\t\t\t\tconst liContent = liMatch[1] || \"\";\n\t\t\t\t\tconst { children, markDefs } = parseInlineContent(liContent, generateKey);\n\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\tstyle: \"normal\",\n\t\t\t\t\t\tlistItem,\n\t\t\t\t\t\tlevel: 1,\n\t\t\t\t\t\tchildren,\n\t\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"hr\": {\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"break\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tstyle: \"lineBreak\",\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"figure\": {\n\t\t\t\t// Check for image\n\t\t\t\tconst imgMatch = content.match(IMG_TAG_PATTERN);\n\t\t\t\tif (imgMatch) {\n\t\t\t\t\tconst srcMatch = imgMatch[0].match(SRC_ATTR_PATTERN);\n\t\t\t\t\tconst altMatch = imgMatch[0].match(ALT_ATTR_PATTERN);\n\t\t\t\t\tconst captionMatch = content.match(FIGCAPTION_TAG_PATTERN);\n\t\t\t\t\tconst imgUrl = srcMatch?.[1] ? decodeUrlEntities(srcMatch[1]) : \"\";\n\n\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\tasset: {\n\t\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t\t_ref: imgUrl,\n\t\t\t\t\t\t\turl: imgUrl || undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t\talt: altMatch?.[1],\n\t\t\t\t\t\tcaption: captionMatch?.[1]?.replace(HTML_TAG_PATTERN, \"\").trim(),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle remaining text\n\tconst remaining = html.slice(lastIndex).trim();\n\tif (remaining) {\n\t\tconst { children, markDefs } = parseInlineContent(remaining, generateKey);\n\t\tif (children.some((c) => c.text.trim())) {\n\t\t\tblocks.push({\n\t\t\t\t_type: \"block\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\tstyle: \"normal\",\n\t\t\t\tchildren,\n\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Create transform context for recursive block transformation\n */\nfunction createTransformContext(\n\toptions: ConvertOptions,\n\tgenerateKey: () => string,\n): TransformContext {\n\tconst context: TransformContext = {\n\t\tgenerateKey,\n\t\tparseInlineContent: (html: string) => parseInlineContent(html, generateKey),\n\t\ttransformBlocks: (blocks: GutenbergBlock[]) =>\n\t\t\tblocks.flatMap((block) => transformBlock(block, options, context)),\n\t};\n\treturn context;\n}\n\n/**\n * Transform a single block\n */\nfunction transformBlock(\n\tblock: GutenbergBlock,\n\toptions: ConvertOptions,\n\tcontext: TransformContext,\n): PortableTextBlock[] {\n\tconst transformer = getTransformer(block.blockName, options.customTransformers);\n\treturn transformer(block, options, context);\n}\n\n/**\n * Decode HTML entities\n */\nfunction decodeHtmlEntities(html: string): string {\n\treturn html\n\t\t.replace(LESS_THAN_ENTITY_PATTERN, \"<\")\n\t\t.replace(GREATER_THAN_ENTITY_PATTERN, \">\")\n\t\t.replace(AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(QUOTE_ENTITY_PATTERN, '\"')\n\t\t.replace(APOS_ENTITY_PATTERN, \"'\")\n\t\t.replace(NUMERIC_AMP_ENTITY_PATTERN, \"&\") // &#038; or &#38;\n\t\t.replace(HEX_AMP_ENTITY_PATTERN, \"&\") // &#x26;\n\t\t.replace(NBSP_ENTITY_PATTERN, \" \");\n}\n\n/**\n * Decode HTML entities in URLs (used for image src attributes)\n */\nfunction decodeUrlEntities(url: string): string {\n\treturn url\n\t\t.replace(AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(NUMERIC_AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(HEX_AMP_ENTITY_PATTERN, \"&\");\n}\n\n/**\n * Parse Gutenberg blocks without converting to Portable Text\n * Useful for inspection and debugging\n */\nexport function parseGutenbergBlocks(content: string): GutenbergBlock[] {\n\tif (!content || !content.trim()) {\n\t\treturn [];\n\t}\n\treturn normalizeBlocks(parse(content));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["decodeUrlEntities","AMP_ENTITY_PATTERN","APOS_ENTITY_PATTERN","NBSP_ENTITY_PATTERN","decodeHtmlEntities","core.paragraph","core.heading","core.list","core.quote","core.code","core.preformatted","core.pullquote","core.verse","core.image","core.gallery","core.file","core.mediaText","core.cover","core.columns","core.group","core.separator","core.table","core.buttons","core.button","core.more","core.nextpage","core.html","core.shortcode","embed.embed","embed.video","embed.audio","embed.youtube","embed.twitter","embed.vimeo"],"sources":["../src/url.ts","../src/inline.ts","../src/types.ts","../src/transformers/core.ts","../src/transformers/embed.ts","../src/transformers/index.ts","../src/index.ts"],"sourcesContent":["/**\n * URL scheme validation for the converter pipeline (defense-in-depth).\n *\n * This mirrors the canonical sanitizeHref in packages/core/src/utils/url.ts.\n * The converter is a standalone zero-dependency package, so it carries its own\n * copy. The render layer in core is the primary defense; this is secondary.\n */\n\nconst SAFE_URL_SCHEME_RE = /^(https?:|mailto:|tel:|\\/(?!\\/)|#)/i;\n\n/**\n * Returns the URL unchanged if it uses a safe scheme, otherwise returns \"\".\n *\n * Returns empty string (not \"#\") because this is the converter layer — we\n * strip bad URLs rather than substituting anchors. The render layer handles\n * the fallback to \"#\".\n */\nexport function sanitizeHref(url: string | undefined | null): string {\n\tif (!url) return \"\";\n\treturn SAFE_URL_SCHEME_RE.test(url) ? url : \"\";\n}\n","/**\n * Inline HTML to Portable Text spans converter\n *\n * Parses inline HTML elements (strong, em, a, code, etc.) and converts\n * them to Portable Text spans with marks.\n */\n\nimport { parseFragment, type DefaultTreeAdapterMap } from \"parse5\";\n\nimport type { PortableTextSpan, PortableTextMarkDef } from \"./types.js\";\nimport { sanitizeHref } from \"./url.js\";\n\n// Regex patterns for inline parsing\nconst WHITESPACE_PATTERN = /\\S/;\n\n// Pre-compiled block tag patterns\nconst BLOCK_TAG_PATTERNS: Record<string, { open: RegExp; close: RegExp }> = {\n\tp: { open: /^<p[^>]*>/i, close: /<\\/p>$/i },\n\th1: { open: /^<h1[^>]*>/i, close: /<\\/h1>$/i },\n\th2: { open: /^<h2[^>]*>/i, close: /<\\/h2>$/i },\n\th3: { open: /^<h3[^>]*>/i, close: /<\\/h3>$/i },\n\th4: { open: /^<h4[^>]*>/i, close: /<\\/h4>$/i },\n\th5: { open: /^<h5[^>]*>/i, close: /<\\/h5>$/i },\n\th6: { open: /^<h6[^>]*>/i, close: /<\\/h6>$/i },\n\tli: { open: /^<li[^>]*>/i, close: /<\\/li>$/i },\n\tblockquote: { open: /^<blockquote[^>]*>/i, close: /<\\/blockquote>$/i },\n\tfigcaption: { open: /^<figcaption[^>]*>/i, close: /<\\/figcaption>$/i },\n};\n\n// Regex patterns for extracting attributes\nconst IMG_ALT_PATTERN = /<img[^>]+alt=[\"']([^\"']*)[\"']/i;\nconst FIGCAPTION_PATTERN = /<figcaption[^>]*>([\\s\\S]*?)<\\/figcaption>/i;\nconst IMG_SRC_PATTERN = /<img[^>]+src=[\"']([^\"']*)[\"']/i;\nconst URL_AMP_ENTITY_PATTERN = /&amp;/g;\nconst URL_NUMERIC_AMP_ENTITY_PATTERN = /&#0?38;/g;\nconst URL_HEX_AMP_ENTITY_PATTERN = /&#x26;/gi;\n\ntype Node = DefaultTreeAdapterMap[\"node\"];\ntype TextNode = DefaultTreeAdapterMap[\"textNode\"];\ntype Element = DefaultTreeAdapterMap[\"element\"];\n\ninterface ParseResult {\n\tchildren: PortableTextSpan[];\n\tmarkDefs: PortableTextMarkDef[];\n}\n\n/**\n * Parse inline HTML content into Portable Text spans\n */\nexport function parseInlineContent(html: string, generateKey: () => string): ParseResult {\n\tconst children: PortableTextSpan[] = [];\n\tconst markDefs: PortableTextMarkDef[] = [];\n\tconst markDefMap = new Map<string, string>();\n\n\t// Handle whitespace-only input BEFORE stripping (parse5 normalizes whitespace away)\n\tif (html.length > 0 && !WHITESPACE_PATTERN.test(html)) {\n\t\treturn {\n\t\t\tchildren: [{ _type: \"span\", _key: generateKey(), text: html }],\n\t\t\tmarkDefs: [],\n\t\t};\n\t}\n\n\t// Strip wrapping tags like <p>, <h1>, etc.\n\tconst strippedHtml = stripBlockTags(html);\n\n\t// Parse HTML fragment\n\tconst fragment = parseFragment(strippedHtml);\n\n\t// Walk the tree and build spans\n\twalkNodes(fragment.childNodes, [], children, markDefs, markDefMap, generateKey);\n\n\t// Ensure at least one span exists\n\tif (children.length === 0) {\n\t\tchildren.push({\n\t\t\t_type: \"span\",\n\t\t\t_key: generateKey(),\n\t\t\ttext: \"\",\n\t\t});\n\t}\n\n\treturn { children, markDefs };\n}\n\n/**\n * Strip common block-level wrapper tags\n */\nfunction stripBlockTags(html: string): string {\n\t// Remove leading/trailing whitespace\n\tlet stripped = html.trim();\n\n\t// Strip common block wrappers\n\tconst blockTags = [\"p\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"li\", \"blockquote\", \"figcaption\"];\n\n\tfor (const tag of blockTags) {\n\t\tconst patterns = BLOCK_TAG_PATTERNS[tag];\n\t\tif (patterns && patterns.open.test(stripped) && patterns.close.test(stripped)) {\n\t\t\tstripped = stripped.replace(patterns.open, \"\").replace(patterns.close, \"\").trim();\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn stripped;\n}\n\n/**\n * Recursively walk DOM nodes and build spans\n */\nfunction walkNodes(\n\tnodes: Node[],\n\tcurrentMarks: string[],\n\tchildren: PortableTextSpan[],\n\tmarkDefs: PortableTextMarkDef[],\n\tmarkDefMap: Map<string, string>,\n\tgenerateKey: () => string,\n): void {\n\tfor (const node of nodes) {\n\t\tif (isTextNode(node)) {\n\t\t\tconst text = node.value;\n\t\t\tif (text) {\n\t\t\t\t// Handle line breaks in text\n\t\t\t\tconst parts = text.split(\"\\n\");\n\t\t\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\t\t\tconst part = parts[i];\n\t\t\t\t\tif (part || i > 0) {\n\t\t\t\t\t\t// Add text span\n\t\t\t\t\t\tif (part) {\n\t\t\t\t\t\t\tchildren.push({\n\t\t\t\t\t\t\t\t_type: \"span\",\n\t\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\t\ttext: part,\n\t\t\t\t\t\t\t\tmarks: currentMarks.length > 0 ? [...currentMarks] : undefined,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Add newline (except after last part)\n\t\t\t\t\t\tif (i < parts.length - 1) {\n\t\t\t\t\t\t\t// Append newline to previous span or create new one\n\t\t\t\t\t\t\tif (children.length > 0) {\n\t\t\t\t\t\t\t\tconst lastChild = children.at(-1);\n\t\t\t\t\t\t\t\tif (lastChild) {\n\t\t\t\t\t\t\t\t\tlastChild.text += \"\\n\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tchildren.push({\n\t\t\t\t\t\t\t\t\t_type: \"span\",\n\t\t\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\t\t\ttext: \"\\n\",\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (isElement(node)) {\n\t\t\tconst tagName = node.tagName.toLowerCase();\n\n\t\t\t// Handle <br> as newline\n\t\t\tif (tagName === \"br\") {\n\t\t\t\tif (children.length > 0) {\n\t\t\t\t\tconst lastChild = children.at(-1);\n\t\t\t\t\tif (lastChild) {\n\t\t\t\t\t\tlastChild.text += \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tchildren.push({\n\t\t\t\t\t\t_type: \"span\",\n\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\ttext: \"\\n\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Get mark for this element\n\t\t\tconst markResult = getMarkForElement(node, markDefs, markDefMap, generateKey);\n\t\t\tconst newMarks = markResult ? [...currentMarks, markResult] : currentMarks;\n\n\t\t\t// Recurse into children\n\t\t\twalkNodes(node.childNodes, newMarks, children, markDefs, markDefMap, generateKey);\n\t\t}\n\t}\n}\n\n/**\n * Get the Portable Text mark for an HTML element\n */\nfunction getMarkForElement(\n\telement: Element,\n\tmarkDefs: PortableTextMarkDef[],\n\tmarkDefMap: Map<string, string>,\n\tgenerateKey: () => string,\n): string | null {\n\tconst tagName = element.tagName.toLowerCase();\n\n\tswitch (tagName) {\n\t\tcase \"strong\":\n\t\tcase \"b\":\n\t\t\treturn \"strong\";\n\n\t\tcase \"em\":\n\t\tcase \"i\":\n\t\t\treturn \"em\";\n\n\t\tcase \"u\":\n\t\t\treturn \"underline\";\n\n\t\tcase \"s\":\n\t\tcase \"strike\":\n\t\tcase \"del\":\n\t\t\treturn \"strike-through\";\n\n\t\tcase \"code\":\n\t\t\treturn \"code\";\n\n\t\tcase \"sup\":\n\t\t\treturn \"superscript\";\n\n\t\tcase \"sub\":\n\t\t\treturn \"subscript\";\n\n\t\tcase \"a\": {\n\t\t\tconst href = sanitizeHref(getAttr(element, \"href\"));\n\t\t\tconst target = getAttr(element, \"target\");\n\n\t\t\t// Check if we already have a markDef for this href\n\t\t\tconst existingKey = markDefMap.get(href);\n\t\t\tif (existingKey) {\n\t\t\t\treturn existingKey;\n\t\t\t}\n\n\t\t\t// Create new mark definition\n\t\t\tconst key = generateKey();\n\t\t\tconst markDef: PortableTextMarkDef = {\n\t\t\t\t_type: \"link\",\n\t\t\t\t_key: key,\n\t\t\t\thref,\n\t\t\t};\n\t\t\tif (target === \"_blank\") {\n\t\t\t\tmarkDef.blank = true;\n\t\t\t}\n\t\t\tmarkDefs.push(markDef);\n\t\t\tmarkDefMap.set(href, key);\n\t\t\treturn key;\n\t\t}\n\n\t\tdefault:\n\t\t\t// Unknown inline element - ignore the tag, process children\n\t\t\treturn null;\n\t}\n}\n\n/**\n * Get attribute value from element\n */\nfunction getAttr(element: Element, name: string): string | undefined {\n\tconst attr = element.attrs.find((a) => a.name.toLowerCase() === name);\n\treturn attr?.value;\n}\n\n/**\n * Type guard for text nodes\n */\nfunction isTextNode(node: Node): node is TextNode {\n\treturn node.nodeName === \"#text\";\n}\n\n/**\n * Type guard for elements\n */\nfunction isElement(node: Node): node is Element {\n\treturn \"tagName\" in node;\n}\n\n/**\n * Extract plain text from HTML (for alt text, captions)\n */\nexport function extractText(html: string): string {\n\tconst fragment = parseFragment(html);\n\treturn getTextContent(fragment.childNodes);\n}\n\nfunction getTextContent(nodes: Node[]): string {\n\tlet text = \"\";\n\tfor (const node of nodes) {\n\t\tif (isTextNode(node)) {\n\t\t\ttext += node.value;\n\t\t} else if (isElement(node)) {\n\t\t\ttext += getTextContent(node.childNodes);\n\t\t}\n\t}\n\treturn text.trim();\n}\n\n/**\n * Extract alt text from an img element in HTML\n */\nexport function extractAlt(html: string): string | undefined {\n\tconst match = html.match(IMG_ALT_PATTERN);\n\tif (match) {\n\t\treturn match[1]; // Can be empty string \"\"\n\t}\n\treturn undefined;\n}\n\n/**\n * Extract caption from a figcaption element\n */\nexport function extractCaption(html: string): string | undefined {\n\tconst match = html.match(FIGCAPTION_PATTERN);\n\tif (match?.[1]) {\n\t\treturn extractText(match[1]);\n\t}\n\treturn undefined;\n}\n\n/**\n * Extract src from an img element\n */\nexport function extractSrc(html: string): string | undefined {\n\tconst match = html.match(IMG_SRC_PATTERN);\n\tif (!match?.[1]) return undefined;\n\t// Decode HTML entities in URLs\n\treturn decodeUrlEntities(match[1]);\n}\n\n/**\n * Decode HTML entities commonly found in URLs\n */\nfunction decodeUrlEntities(url: string): string {\n\treturn url\n\t\t.replace(URL_AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(URL_NUMERIC_AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(URL_HEX_AMP_ENTITY_PATTERN, \"&\");\n}\n","/**\n * Types for Gutenberg to Portable Text conversion\n */\n\n/**\n * Gutenberg block as parsed by @wordpress/block-serialization-default-parser\n */\nexport interface GutenbergBlock {\n\t/** Block name like \"core/paragraph\" or null for freeform HTML */\n\tblockName: string | null;\n\t/** Block attributes from the JSON comment */\n\tattrs: Record<string, unknown>;\n\t/** Inner HTML content */\n\tinnerHTML: string;\n\t/** Nested blocks (for columns, groups, etc.) */\n\tinnerBlocks: GutenbergBlock[];\n\t/** Content parts between inner blocks */\n\tinnerContent: Array<string | null>;\n}\n\n/**\n * Portable Text span (inline text with marks)\n */\nexport interface PortableTextSpan {\n\t_type: \"span\";\n\t_key: string;\n\ttext: string;\n\tmarks?: string[];\n}\n\n/**\n * Portable Text mark definition (for links, annotations)\n */\nexport interface PortableTextMarkDef {\n\t_type: string;\n\t_key: string;\n\t[key: string]: unknown;\n}\n\n/**\n * Portable Text text block\n */\nexport interface PortableTextTextBlock {\n\t_type: \"block\";\n\t_key: string;\n\tstyle?: \"normal\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"blockquote\";\n\tlistItem?: \"bullet\" | \"number\";\n\tlevel?: number;\n\tchildren: PortableTextSpan[];\n\tmarkDefs?: PortableTextMarkDef[];\n}\n\n/**\n * Portable Text image block\n */\nexport interface PortableTextImageBlock {\n\t_type: \"image\";\n\t_key: string;\n\tasset: {\n\t\t_type: \"reference\";\n\t\t_ref: string;\n\t\turl?: string;\n\t};\n\talt?: string;\n\tcaption?: string;\n\talignment?: \"left\" | \"center\" | \"right\" | \"wide\" | \"full\";\n\tlink?: string;\n}\n\n/**\n * Portable Text code block\n */\nexport interface PortableTextCodeBlock {\n\t_type: \"code\";\n\t_key: string;\n\tcode: string;\n\tlanguage?: string;\n}\n\n/**\n * Portable Text embed block (YouTube, Twitter, etc.)\n */\nexport interface PortableTextEmbedBlock {\n\t_type: \"embed\";\n\t_key: string;\n\turl: string;\n\tprovider?: string;\n\thtml?: string;\n}\n\n/**\n * Portable Text gallery block\n */\nexport interface PortableTextGalleryBlock {\n\t_type: \"gallery\";\n\t_key: string;\n\timages: Array<{\n\t\t_type: \"image\";\n\t\t_key: string;\n\t\tasset: { _type: \"reference\"; _ref: string; url?: string };\n\t\talt?: string;\n\t\tcaption?: string;\n\t}>;\n\tcolumns?: number;\n}\n\n/**\n * Portable Text columns block\n */\nexport interface PortableTextColumnsBlock {\n\t_type: \"columns\";\n\t_key: string;\n\tcolumns: Array<{\n\t\t_type: \"column\";\n\t\t_key: string;\n\t\tcontent: PortableTextBlock[];\n\t}>;\n}\n\n/**\n * Portable Text break/divider block\n */\nexport interface PortableTextBreakBlock {\n\t_type: \"break\";\n\t_key: string;\n\tstyle: \"lineBreak\";\n}\n\n/**\n * Portable Text table block\n */\nexport interface PortableTextTableBlock {\n\t_type: \"table\";\n\t_key: string;\n\trows: Array<{\n\t\t_type: \"tableRow\";\n\t\t_key: string;\n\t\tcells: Array<{\n\t\t\t_type: \"tableCell\";\n\t\t\t_key: string;\n\t\t\tcontent: PortableTextSpan[];\n\t\t\tmarkDefs?: PortableTextMarkDef[];\n\t\t\tisHeader?: boolean;\n\t\t}>;\n\t}>;\n\thasHeaderRow?: boolean;\n}\n\n/**\n * Fallback HTML block for unconvertible content\n */\nexport interface PortableTextHtmlBlock {\n\t_type: \"htmlBlock\";\n\t_key: string;\n\thtml: string;\n\toriginalBlockName?: string | null;\n\toriginalAttrs?: Record<string, unknown>;\n}\n\n/**\n * Portable Text button block\n */\nexport interface PortableTextButtonBlock {\n\t_type: \"button\";\n\t_key: string;\n\ttext: string;\n\turl?: string;\n\tstyle?: \"default\" | \"outline\" | \"fill\";\n}\n\n/**\n * Portable Text buttons container block\n */\nexport interface PortableTextButtonsBlock {\n\t_type: \"buttons\";\n\t_key: string;\n\tbuttons: PortableTextButtonBlock[];\n\tlayout?: \"horizontal\" | \"vertical\";\n}\n\n/**\n * Portable Text cover block (image/video with text overlay)\n */\nexport interface PortableTextCoverBlock {\n\t_type: \"cover\";\n\t_key: string;\n\tbackgroundImage?: string;\n\tbackgroundVideo?: string;\n\toverlayColor?: string;\n\toverlayOpacity?: number;\n\tcontent: PortableTextBlock[];\n\tminHeight?: string;\n\talignment?: \"left\" | \"center\" | \"right\";\n}\n\n/**\n * Portable Text file download block\n */\nexport interface PortableTextFileBlock {\n\t_type: \"file\";\n\t_key: string;\n\turl: string;\n\tfilename?: string;\n\tshowDownloadButton?: boolean;\n}\n\n/**\n * Portable Text pullquote block\n */\nexport interface PortableTextPullquoteBlock {\n\t_type: \"pullquote\";\n\t_key: string;\n\ttext: string;\n\tcitation?: string;\n}\n\n/**\n * Union of all Portable Text block types\n */\nexport type PortableTextBlock =\n\t| PortableTextTextBlock\n\t| PortableTextImageBlock\n\t| PortableTextCodeBlock\n\t| PortableTextEmbedBlock\n\t| PortableTextGalleryBlock\n\t| PortableTextColumnsBlock\n\t| PortableTextBreakBlock\n\t| PortableTextTableBlock\n\t| PortableTextHtmlBlock\n\t| PortableTextButtonBlock\n\t| PortableTextButtonsBlock\n\t| PortableTextCoverBlock\n\t| PortableTextFileBlock\n\t| PortableTextPullquoteBlock;\n\n/**\n * Options for the conversion\n */\nexport interface ConvertOptions {\n\t/** Map of WordPress media IDs to EmDash media IDs/URLs */\n\tmediaMap?: Map<number, string>;\n\t/** Custom block transformers */\n\tcustomTransformers?: Record<string, BlockTransformer>;\n\t/** Whether to generate keys (default: true) */\n\tgenerateKeys?: boolean;\n\t/** Custom key generator */\n\tkeyGenerator?: () => string;\n}\n\n/**\n * Block transformer function\n */\nexport type BlockTransformer = (\n\tblock: GutenbergBlock,\n\toptions: ConvertOptions,\n\tcontext: TransformContext,\n) => PortableTextBlock[];\n\n/**\n * Context passed to transformers\n */\nexport interface TransformContext {\n\t/** Transform child blocks recursively */\n\ttransformBlocks: (blocks: GutenbergBlock[]) => PortableTextBlock[];\n\t/** Parse inline HTML to spans */\n\tparseInlineContent: (html: string) => {\n\t\tchildren: PortableTextSpan[];\n\t\tmarkDefs: PortableTextMarkDef[];\n\t};\n\t/** Generate a unique key */\n\tgenerateKey: () => string;\n}\n\n// ── Attribute accessor helpers ──────────────────────────────────────\n// Gutenberg attrs are Record<string, unknown>. These narrow safely\n// without `as` casts.\n\n/** Extract a string attribute, returning undefined if missing or wrong type */\nexport function attrString(attrs: Record<string, unknown>, key: string): string | undefined {\n\tconst v = attrs[key];\n\treturn typeof v === \"string\" ? v : undefined;\n}\n\n/** Extract a number attribute, returning undefined if missing or wrong type */\nexport function attrNumber(attrs: Record<string, unknown>, key: string): number | undefined {\n\tconst v = attrs[key];\n\treturn typeof v === \"number\" ? v : undefined;\n}\n\n/** Extract a boolean attribute, returning undefined if missing or wrong type */\nexport function attrBoolean(attrs: Record<string, unknown>, key: string): boolean | undefined {\n\tconst v = attrs[key];\n\treturn typeof v === \"boolean\" ? v : undefined;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n\treturn typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n/** Extract an object attribute, returning undefined if missing or wrong type */\nexport function attrObject(\n\tattrs: Record<string, unknown>,\n\tkey: string,\n): Record<string, unknown> | undefined {\n\tconst v = attrs[key];\n\treturn isRecord(v) ? v : undefined;\n}\n","/**\n * Transformers for WordPress core/* blocks\n */\n\nimport { extractAlt, extractCaption, extractSrc, extractText } from \"../inline.js\";\nimport type {\n\tGutenbergBlock,\n\tPortableTextBlock,\n\tPortableTextTextBlock,\n\tBlockTransformer,\n\tTransformContext,\n} from \"../types.js\";\nimport { attrString, attrNumber, attrBoolean, attrObject } from \"../types.js\";\nimport { sanitizeHref } from \"../url.js\";\n\n// Regex patterns for core block transformers\nconst UOL_TAG_PATTERN = /<[uo]l[^>]*>([\\s\\S]*)<\\/[uo]l>/i;\nconst LI_TAG_PATTERN = /<li[^>]*>([\\s\\S]*?)<\\/li>/i;\nconst UL_TAG_PATTERN = /<ul[^>]*>([\\s\\S]*)<\\/ul>/i;\nconst OL_TAG_PATTERN = /<ol[^>]*>([\\s\\S]*)<\\/ol>/i;\nconst NESTED_LIST_PATTERN = /<[uo]l[^>]*>[\\s\\S]*<\\/[uo]l>/gi;\nconst P_TAG_PATTERN = /<p[^>]*>([\\s\\S]*?)<\\/p>/gi;\nconst P_TAG_SINGLE_PATTERN = /<p[^>]*>([\\s\\S]*?)<\\/p>/i;\nconst HREF_PATTERN = /href=\"([^\"]*)\"/i;\nconst DATA_ID_PATTERN = /data-id=[\"'](\\d+)[\"']/i;\nconst CODE_TAG_PATTERN_SINGLE = /<code[^>]*>([\\s\\S]*?)<\\/code>/i;\nconst TABLE_TAG_PATTERN = /<table[^>]*>([\\s\\S]*?)<\\/table>/i;\nconst THEAD_TAG_PATTERN = /<thead[^>]*>([\\s\\S]*?)<\\/thead>/i;\nconst IMG_TAG_GLOBAL = /<img[^>]+>/gi;\nconst TABLE_ROW_PATTERN = /<tr[^>]*>([\\s\\S]*?)<\\/tr>/gi;\nconst TABLE_CELL_PATTERN = /<(th|td)[^>]*>([\\s\\S]*?)<\\/\\1>/gi;\nconst TBODY_TAG_PATTERN = /<tbody[^>]*>([\\s\\S]*?)<\\/tbody>/i;\nconst CITE_TAG_PATTERN = /<cite[^>]*>([\\s\\S]*?)<\\/cite>/i;\nconst LT_ENTITY_PATTERN = /&lt;/g;\nconst GT_ENTITY_PATTERN = /&gt;/g;\nconst AMP_ENTITY_PATTERN = /&amp;/g;\nconst QUOT_ENTITY_PATTERN = /&quot;/g;\nconst APOS_ENTITY_PATTERN = /&#039;/g;\nconst NBSP_ENTITY_PATTERN = /&nbsp;/g;\n\n/**\n * core/paragraph → block with style \"normal\"\n */\nexport const paragraph: BlockTransformer = (block, _options, context) => {\n\tconst { children, markDefs } = context.parseInlineContent(block.innerHTML);\n\n\t// Skip empty paragraphs\n\tif (children.length === 1 && children[0]?.text === \"\") {\n\t\treturn [];\n\t}\n\n\tconst result: PortableTextTextBlock = {\n\t\t_type: \"block\",\n\t\t_key: context.generateKey(),\n\t\tstyle: \"normal\",\n\t\tchildren,\n\t};\n\n\tif (markDefs.length > 0) {\n\t\tresult.markDefs = markDefs;\n\t}\n\n\treturn [result];\n};\n\n/**\n * core/heading → block with style \"h1\"-\"h6\"\n */\nexport const heading: BlockTransformer = (block, _options, context) => {\n\tconst level = attrNumber(block.attrs, \"level\") ?? 2;\n\tconst { children, markDefs } = context.parseInlineContent(block.innerHTML);\n\n\tconst result: PortableTextTextBlock = {\n\t\t_type: \"block\",\n\t\t_key: context.generateKey(),\n\t\tstyle: toHeadingStyle(level),\n\t\tchildren,\n\t};\n\n\tif (markDefs.length > 0) {\n\t\tresult.markDefs = markDefs;\n\t}\n\n\treturn [result];\n};\n\n/**\n * core/list → blocks with listItem\n *\n * Handles both old format (HTML list) and new format (innerBlocks with list-item)\n */\nexport const list: BlockTransformer = (block, _options, context) => {\n\tconst ordered = block.attrs.ordered === true;\n\tconst listItem = ordered ? \"number\" : \"bullet\";\n\n\t// Check for new format (WordPress 6.x) with core/list-item innerBlocks\n\tif (block.innerBlocks.length > 0) {\n\t\treturn parseListItemBlocks(block.innerBlocks, listItem, 1, context);\n\t}\n\n\t// Old format: HTML content in innerHTML\n\tconst listMatch = block.innerHTML.match(UOL_TAG_PATTERN);\n\tconst listContent = listMatch?.[1] || block.innerHTML;\n\n\treturn parseListItems(listContent, listItem, 1, context);\n};\n\n/**\n * Parse list-item blocks (WordPress 6.x format)\n */\nfunction parseListItemBlocks(\n\tinnerBlocks: GutenbergBlock[],\n\tlistItem: \"bullet\" | \"number\",\n\tlevel: number,\n\tcontext: TransformContext,\n): PortableTextTextBlock[] {\n\tconst blocks: PortableTextTextBlock[] = [];\n\n\tfor (const itemBlock of innerBlocks) {\n\t\tif (itemBlock.blockName !== \"core/list-item\") continue;\n\n\t\t// Get text content from the <li> in innerHTML\n\t\tconst textMatch = itemBlock.innerHTML.match(LI_TAG_PATTERN);\n\t\tconst textContent = textMatch?.[1]?.trim() || \"\";\n\n\t\tif (textContent) {\n\t\t\tconst { children, markDefs } = context.parseInlineContent(textContent);\n\n\t\t\tconst block: PortableTextTextBlock = {\n\t\t\t\t_type: \"block\",\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\tstyle: \"normal\",\n\t\t\t\tlistItem,\n\t\t\t\tlevel,\n\t\t\t\tchildren,\n\t\t\t};\n\n\t\t\tif (markDefs.length > 0) {\n\t\t\t\tblock.markDefs = markDefs;\n\t\t\t}\n\n\t\t\tblocks.push(block);\n\t\t}\n\n\t\t// Handle nested lists in innerBlocks\n\t\tif (itemBlock.innerBlocks.length > 0) {\n\t\t\tfor (const nested of itemBlock.innerBlocks) {\n\t\t\t\tif (nested.blockName === \"core/list\") {\n\t\t\t\t\tconst nestedOrdered = nested.attrs.ordered === true;\n\t\t\t\t\tconst nestedListItem = nestedOrdered ? \"number\" : \"bullet\";\n\t\t\t\t\tblocks.push(\n\t\t\t\t\t\t...parseListItemBlocks(nested.innerBlocks, nestedListItem, level + 1, context),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Parse list items from HTML\n */\nfunction parseListItems(\n\thtml: string,\n\tlistItem: \"bullet\" | \"number\",\n\tlevel: number,\n\tcontext: TransformContext,\n): PortableTextTextBlock[] {\n\tconst blocks: PortableTextTextBlock[] = [];\n\n\t// Match <li> elements - need to handle nested lists carefully\n\t// Find each top-level <li> by tracking tag depth\n\tconst liItems = extractTopLevelListItems(html);\n\n\tfor (const liContent of liItems) {\n\t\t// Check for nested lists\n\t\tconst nestedUl = liContent.match(UL_TAG_PATTERN);\n\t\tconst nestedOl = liContent.match(OL_TAG_PATTERN);\n\n\t\t// Get text content (excluding nested lists)\n\t\tlet textContent = liContent.replace(NESTED_LIST_PATTERN, \"\").trim();\n\n\t\tif (textContent) {\n\t\t\tconst { children, markDefs } = context.parseInlineContent(textContent);\n\n\t\t\tconst block: PortableTextTextBlock = {\n\t\t\t\t_type: \"block\",\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\tstyle: \"normal\",\n\t\t\t\tlistItem,\n\t\t\t\tlevel,\n\t\t\t\tchildren,\n\t\t\t};\n\n\t\t\tif (markDefs.length > 0) {\n\t\t\t\tblock.markDefs = markDefs;\n\t\t\t}\n\n\t\t\tblocks.push(block);\n\t\t}\n\n\t\t// Process nested lists\n\t\tif (nestedUl?.[1]) {\n\t\t\tblocks.push(...parseListItems(nestedUl[1], \"bullet\", level + 1, context));\n\t\t}\n\t\tif (nestedOl?.[1]) {\n\t\t\tblocks.push(...parseListItems(nestedOl[1], \"number\", level + 1, context));\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Extract top-level <li> items from HTML, handling nested lists correctly\n */\nfunction extractTopLevelListItems(html: string): string[] {\n\tconst items: string[] = [];\n\tlet depth = 0;\n\tlet currentItem = \"\";\n\tlet inLi = false;\n\tlet i = 0;\n\n\twhile (i < html.length) {\n\t\t// Check for opening tags\n\t\tif (html.substring(i, i + 3).toLowerCase() === \"<li\") {\n\t\t\t// Find end of tag\n\t\t\tconst tagEnd = html.indexOf(\">\", i);\n\t\t\tif (tagEnd === -1) break;\n\n\t\t\tif (!inLi) {\n\t\t\t\tinLi = true;\n\t\t\t\ti = tagEnd + 1;\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\t// Nested li\n\t\t\t\tcurrentItem += html.substring(i, tagEnd + 1);\n\t\t\t\tdepth++;\n\t\t\t\ti = tagEnd + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Check for closing </li>\n\t\tif (html.substring(i, i + 5).toLowerCase() === \"</li>\") {\n\t\t\tif (depth === 0) {\n\t\t\t\t// End of current top-level item\n\t\t\t\titems.push(currentItem);\n\t\t\t\tcurrentItem = \"\";\n\t\t\t\tinLi = false;\n\t\t\t\ti += 5;\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\t// End of nested item\n\t\t\t\tcurrentItem += \"</li>\";\n\t\t\t\tdepth--;\n\t\t\t\ti += 5;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Check for nested list tags to track depth\n\t\tif (\n\t\t\thtml.substring(i, i + 3).toLowerCase() === \"<ul\" ||\n\t\t\thtml.substring(i, i + 3).toLowerCase() === \"<ol\"\n\t\t) {\n\t\t\tconst tagEnd = html.indexOf(\">\", i);\n\t\t\tif (tagEnd !== -1) {\n\t\t\t\tcurrentItem += html.substring(i, tagEnd + 1);\n\t\t\t\ti = tagEnd + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\thtml.substring(i, i + 5).toLowerCase() === \"</ul>\" ||\n\t\t\thtml.substring(i, i + 5).toLowerCase() === \"</ol>\"\n\t\t) {\n\t\t\tcurrentItem += html.substring(i, i + 5);\n\t\t\ti += 5;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Regular character\n\t\tif (inLi) {\n\t\t\tcurrentItem += html[i];\n\t\t}\n\t\ti++;\n\t}\n\n\t// Handle case where closing </li> is missing\n\tif (currentItem.trim()) {\n\t\titems.push(currentItem);\n\t}\n\n\t// Filter out empty items that may result from whitespace between tags\n\treturn items.filter((item) => item.trim().length > 0);\n}\n\n/**\n * core/quote → block with style \"blockquote\"\n */\nexport const quote: BlockTransformer = (block, _options, context) => {\n\tconst blocks: PortableTextBlock[] = [];\n\n\t// Extract paragraphs from the blockquote\n\tlet match;\n\n\twhile ((match = P_TAG_PATTERN.exec(block.innerHTML)) !== null) {\n\t\tconst content = match[1] || \"\";\n\t\tconst { children, markDefs } = context.parseInlineContent(content);\n\n\t\tconst quoteBlock: PortableTextTextBlock = {\n\t\t\t_type: \"block\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"blockquote\",\n\t\t\tchildren,\n\t\t};\n\n\t\tif (markDefs.length > 0) {\n\t\t\tquoteBlock.markDefs = markDefs;\n\t\t}\n\n\t\tblocks.push(quoteBlock);\n\t}\n\n\t// If no paragraphs found, treat entire content as quote\n\tif (blocks.length === 0) {\n\t\tconst { children, markDefs } = context.parseInlineContent(block.innerHTML);\n\n\t\tconst quoteBlock: PortableTextTextBlock = {\n\t\t\t_type: \"block\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"blockquote\",\n\t\t\tchildren,\n\t\t};\n\n\t\tif (markDefs.length > 0) {\n\t\t\tquoteBlock.markDefs = markDefs;\n\t\t}\n\n\t\tblocks.push(quoteBlock);\n\t}\n\n\t// Handle citation if present\n\tconst citation = attrString(block.attrs, \"citation\");\n\tif (citation) {\n\t\tconst { children, markDefs } = context.parseInlineContent(citation);\n\n\t\tconst citationBlock: PortableTextTextBlock = {\n\t\t\t_type: \"block\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"normal\",\n\t\t\tchildren: [\n\t\t\t\t{\n\t\t\t\t\t_type: \"span\",\n\t\t\t\t\t_key: context.generateKey(),\n\t\t\t\t\ttext: \"— \",\n\t\t\t\t},\n\t\t\t\t...children,\n\t\t\t],\n\t\t};\n\n\t\tif (markDefs.length > 0) {\n\t\t\tcitationBlock.markDefs = markDefs;\n\t\t}\n\n\t\tblocks.push(citationBlock);\n\t}\n\n\treturn blocks;\n};\n\n/**\n * core/image → image block\n */\nexport const image: BlockTransformer = (block, options, context) => {\n\tconst wpId = attrNumber(block.attrs, \"id\");\n\tconst src = attrString(block.attrs, \"url\") ?? extractSrc(block.innerHTML);\n\tconst alt = attrString(block.attrs, \"alt\") ?? extractAlt(block.innerHTML);\n\tconst caption = extractCaption(block.innerHTML);\n\tconst align = attrString(block.attrs, \"align\");\n\n\t// Resolve media ID if we have a map\n\tconst ref = wpId && options.mediaMap?.get(wpId);\n\n\treturn [\n\t\t{\n\t\t\t_type: \"image\",\n\t\t\t_key: context.generateKey(),\n\t\t\tasset: {\n\t\t\t\t_type: \"reference\",\n\t\t\t\t_ref: ref || String(wpId || src || \"\"),\n\t\t\t\turl: src,\n\t\t\t},\n\t\t\talt,\n\t\t\tcaption,\n\t\t\talignment: mapAlignment(align),\n\t\t},\n\t];\n};\n\n/**\n * core/code → code block\n */\nexport const code: BlockTransformer = (block, _options, context) => {\n\t// Extract code from <pre><code>...</code></pre>\n\tconst codeMatch = block.innerHTML.match(CODE_TAG_PATTERN_SINGLE);\n\tconst codeContent = codeMatch?.[1] || block.innerHTML;\n\n\t// Decode HTML entities\n\tconst decoded = decodeHtmlEntities(codeContent);\n\n\treturn [\n\t\t{\n\t\t\t_type: \"code\",\n\t\t\t_key: context.generateKey(),\n\t\t\tcode: decoded,\n\t\t\tlanguage: attrString(block.attrs, \"language\"),\n\t\t},\n\t];\n};\n\n/**\n * core/preformatted → code block (no syntax highlighting)\n */\nexport const preformatted: BlockTransformer = (block, _options, context) => {\n\tconst text = extractText(block.innerHTML);\n\n\treturn [\n\t\t{\n\t\t\t_type: \"code\",\n\t\t\t_key: context.generateKey(),\n\t\t\tcode: text,\n\t\t},\n\t];\n};\n\n/**\n * core/separator / core/spacer → break block\n */\nexport const separator: BlockTransformer = (_block, _options, context) => {\n\treturn [\n\t\t{\n\t\t\t_type: \"break\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"lineBreak\",\n\t\t},\n\t];\n};\n\n/**\n * core/gallery → gallery block\n */\nexport const gallery: BlockTransformer = (block, options, context) => {\n\tconst images: Array<{\n\t\t_type: \"image\";\n\t\t_key: string;\n\t\tasset: { _type: \"reference\"; _ref: string; url?: string };\n\t\talt?: string;\n\t\tcaption?: string;\n\t}> = [];\n\n\t// Extract images from inner blocks or HTML\n\tif (block.innerBlocks.length > 0) {\n\t\tfor (const innerBlock of block.innerBlocks) {\n\t\t\tif (innerBlock.blockName === \"core/image\") {\n\t\t\t\tconst wpId = attrNumber(innerBlock.attrs, \"id\");\n\t\t\t\tconst src = attrString(innerBlock.attrs, \"url\") ?? extractSrc(innerBlock.innerHTML);\n\t\t\t\tconst alt = attrString(innerBlock.attrs, \"alt\") ?? extractAlt(innerBlock.innerHTML);\n\t\t\t\tconst caption = extractCaption(innerBlock.innerHTML);\n\t\t\t\tconst ref = wpId && options.mediaMap?.get(wpId);\n\n\t\t\t\timages.push({\n\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t_key: context.generateKey(),\n\t\t\t\t\tasset: {\n\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t_ref: ref || String(wpId || src || \"\"),\n\t\t\t\t\t\turl: src,\n\t\t\t\t\t},\n\t\t\t\t\talt,\n\t\t\t\t\tcaption,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Parse from HTML (older gallery format)\n\t\tlet match;\n\t\twhile ((match = IMG_TAG_GLOBAL.exec(block.innerHTML)) !== null) {\n\t\t\tconst imgHtml = match[0];\n\t\t\tconst src = extractSrc(imgHtml);\n\t\t\tconst alt = extractAlt(imgHtml);\n\t\t\tconst idMatch = imgHtml.match(DATA_ID_PATTERN);\n\t\t\tconst wpId = idMatch?.[1] ? parseInt(idMatch[1], 10) : undefined;\n\t\t\tconst ref = wpId && options.mediaMap?.get(wpId);\n\n\t\t\timages.push({\n\t\t\t\t_type: \"image\",\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\tasset: {\n\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t_ref: ref || String(wpId || src || \"\"),\n\t\t\t\t\turl: src,\n\t\t\t\t},\n\t\t\t\talt,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn [\n\t\t{\n\t\t\t_type: \"gallery\",\n\t\t\t_key: context.generateKey(),\n\t\t\timages,\n\t\t\tcolumns: attrNumber(block.attrs, \"columns\"),\n\t\t},\n\t];\n};\n\n/**\n * core/columns → columns block\n */\nexport const columns: BlockTransformer = (block, _options, context) => {\n\tconst columnBlocks = block.innerBlocks.map((col) => ({\n\t\t_type: \"column\" as const,\n\t\t_key: context.generateKey(),\n\t\tcontent: context.transformBlocks(col.innerBlocks),\n\t}));\n\n\treturn [\n\t\t{\n\t\t\t_type: \"columns\",\n\t\t\t_key: context.generateKey(),\n\t\t\tcolumns: columnBlocks,\n\t\t},\n\t];\n};\n\n/**\n * core/group → flatten children (no special container)\n */\nexport const group: BlockTransformer = (block, _options, context) => {\n\treturn context.transformBlocks(block.innerBlocks);\n};\n\n/**\n * core/table → table block\n */\nexport const table: BlockTransformer = (block, _options, context) => {\n\t// Parse the table HTML\n\tconst tableMatch = block.innerHTML.match(TABLE_TAG_PATTERN);\n\tif (!tableMatch) {\n\t\treturn [];\n\t}\n\n\tconst tableContent = tableMatch[1]!;\n\n\t// Check for thead\n\tconst theadMatch = tableContent.match(THEAD_TAG_PATTERN);\n\tconst tbodyMatch = tableContent.match(TBODY_TAG_PATTERN);\n\n\tconst rows: Array<{\n\t\t_type: \"tableRow\";\n\t\t_key: string;\n\t\tcells: Array<{\n\t\t\t_type: \"tableCell\";\n\t\t\t_key: string;\n\t\t\tcontent: import(\"../types.js\").PortableTextSpan[];\n\t\t\tmarkDefs?: import(\"../types.js\").PortableTextMarkDef[];\n\t\t\tisHeader?: boolean;\n\t\t}>;\n\t}> = [];\n\n\t// Parse header rows\n\tif (theadMatch?.[1]) {\n\t\tconst headerRows = parseTableRows(theadMatch[1], context, true);\n\t\trows.push(...headerRows);\n\t}\n\n\t// Parse body rows\n\tif (tbodyMatch?.[1]) {\n\t\tconst bodyRows = parseTableRows(tbodyMatch[1], context, false);\n\t\trows.push(...bodyRows);\n\t} else if (!theadMatch) {\n\t\t// No thead or tbody, parse rows directly\n\t\tconst directRows = parseTableRows(tableContent, context, false);\n\t\trows.push(...directRows);\n\t}\n\n\tif (rows.length === 0) {\n\t\treturn [];\n\t}\n\n\treturn [\n\t\t{\n\t\t\t_type: \"table\" as const,\n\t\t\t_key: context.generateKey(),\n\t\t\trows,\n\t\t\thasHeaderRow: !!theadMatch,\n\t\t},\n\t];\n};\n\n/**\n * Parse table rows from HTML\n */\nfunction parseTableRows(\n\thtml: string,\n\tcontext: import(\"../types.js\").TransformContext,\n\tisHeader: boolean,\n): Array<{\n\t_type: \"tableRow\";\n\t_key: string;\n\tcells: Array<{\n\t\t_type: \"tableCell\";\n\t\t_key: string;\n\t\tcontent: import(\"../types.js\").PortableTextSpan[];\n\t\tmarkDefs?: import(\"../types.js\").PortableTextMarkDef[];\n\t\tisHeader?: boolean;\n\t}>;\n}> {\n\tconst rows: Array<{\n\t\t_type: \"tableRow\";\n\t\t_key: string;\n\t\tcells: Array<{\n\t\t\t_type: \"tableCell\";\n\t\t\t_key: string;\n\t\t\tcontent: import(\"../types.js\").PortableTextSpan[];\n\t\t\tmarkDefs?: import(\"../types.js\").PortableTextMarkDef[];\n\t\t\tisHeader?: boolean;\n\t\t}>;\n\t}> = [];\n\n\tlet rowMatch;\n\n\twhile ((rowMatch = TABLE_ROW_PATTERN.exec(html)) !== null) {\n\t\tconst rowContent = rowMatch[1]!;\n\t\tconst cells: Array<{\n\t\t\t_type: \"tableCell\";\n\t\t\t_key: string;\n\t\t\tcontent: import(\"../types.js\").PortableTextSpan[];\n\t\t\tmarkDefs?: import(\"../types.js\").PortableTextMarkDef[];\n\t\t\tisHeader?: boolean;\n\t\t}> = [];\n\n\t\t// Match both th and td cells\n\t\tlet cellMatch;\n\n\t\twhile ((cellMatch = TABLE_CELL_PATTERN.exec(rowContent)) !== null) {\n\t\t\tconst isHeaderCell = cellMatch[1]!.toLowerCase() === \"th\" || isHeader;\n\t\t\tconst cellContent = cellMatch[2]!;\n\n\t\t\tconst { children, markDefs } = context.parseInlineContent(cellContent);\n\n\t\t\tcells.push({\n\t\t\t\t_type: \"tableCell\" as const,\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\tcontent: children,\n\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\tisHeader: isHeaderCell || undefined,\n\t\t\t});\n\t\t}\n\n\t\tif (cells.length > 0) {\n\t\t\trows.push({\n\t\t\t\t_type: \"tableRow\" as const,\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\tcells,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn rows;\n}\n\n/**\n * Convert a heading level number to a PortableTextTextBlock style\n */\nfunction toHeadingStyle(level: number): PortableTextTextBlock[\"style\"] {\n\tswitch (level) {\n\t\tcase 1:\n\t\t\treturn \"h1\";\n\t\tcase 2:\n\t\t\treturn \"h2\";\n\t\tcase 3:\n\t\t\treturn \"h3\";\n\t\tcase 4:\n\t\t\treturn \"h4\";\n\t\tcase 5:\n\t\t\treturn \"h5\";\n\t\tcase 6:\n\t\t\treturn \"h6\";\n\t\tdefault:\n\t\t\treturn \"h2\";\n\t}\n}\n\n/**\n * Map WordPress alignment to Portable Text alignment\n */\nfunction mapAlignment(\n\talign: string | undefined,\n): \"left\" | \"center\" | \"right\" | \"wide\" | \"full\" | undefined {\n\tswitch (align) {\n\t\tcase \"left\":\n\t\tcase \"center\":\n\t\tcase \"right\":\n\t\tcase \"wide\":\n\t\tcase \"full\":\n\t\t\treturn align;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Decode HTML entities\n */\nfunction decodeHtmlEntities(html: string): string {\n\treturn html\n\t\t.replace(LT_ENTITY_PATTERN, \"<\")\n\t\t.replace(GT_ENTITY_PATTERN, \">\")\n\t\t.replace(AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(QUOT_ENTITY_PATTERN, '\"')\n\t\t.replace(APOS_ENTITY_PATTERN, \"'\")\n\t\t.replace(NBSP_ENTITY_PATTERN, \" \");\n}\n\n/**\n * core/button → button block\n */\nexport const button: BlockTransformer = (block, _options, context) => {\n\tconst url = sanitizeHref(attrString(block.attrs, \"url\"));\n\tconst text = extractText(block.innerHTML).trim() || \"Button\";\n\n\t// Detect button style from className\n\tlet style: \"default\" | \"outline\" | \"fill\" = \"default\";\n\tconst className = attrString(block.attrs, \"className\");\n\tif (className?.includes(\"is-style-outline\")) {\n\t\tstyle = \"outline\";\n\t} else if (className?.includes(\"is-style-fill\")) {\n\t\tstyle = \"fill\";\n\t}\n\n\treturn [\n\t\t{\n\t\t\t_type: \"button\",\n\t\t\t_key: context.generateKey(),\n\t\t\ttext,\n\t\t\turl,\n\t\t\tstyle,\n\t\t},\n\t];\n};\n\n/**\n * core/buttons → buttons container block\n */\nexport const buttons: BlockTransformer = (block, _options, context) => {\n\tconst buttonBlocks: Array<{\n\t\t_type: \"button\";\n\t\t_key: string;\n\t\ttext: string;\n\t\turl?: string;\n\t\tstyle?: \"default\" | \"outline\" | \"fill\";\n\t}> = [];\n\n\tfor (const innerBlock of block.innerBlocks) {\n\t\tif (innerBlock.blockName === \"core/button\") {\n\t\t\tconst url = attrString(innerBlock.attrs, \"url\");\n\t\t\tconst text = extractText(innerBlock.innerHTML).trim() || \"Button\";\n\n\t\t\tlet style: \"default\" | \"outline\" | \"fill\" = \"default\";\n\t\t\tconst className = attrString(innerBlock.attrs, \"className\");\n\t\t\tif (className?.includes(\"is-style-outline\")) {\n\t\t\t\tstyle = \"outline\";\n\t\t\t} else if (className?.includes(\"is-style-fill\")) {\n\t\t\t\tstyle = \"fill\";\n\t\t\t}\n\n\t\t\tbuttonBlocks.push({\n\t\t\t\t_type: \"button\",\n\t\t\t\t_key: context.generateKey(),\n\t\t\t\ttext,\n\t\t\t\turl,\n\t\t\t\tstyle,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Detect layout from attrs\n\tconst layoutObj = attrObject(block.attrs, \"layout\");\n\tconst layout =\n\t\tlayoutObj && typeof layoutObj[\"type\"] === \"string\" && layoutObj[\"type\"] === \"flex\"\n\t\t\t? \"horizontal\"\n\t\t\t: \"vertical\";\n\n\treturn [\n\t\t{\n\t\t\t_type: \"buttons\",\n\t\t\t_key: context.generateKey(),\n\t\t\tbuttons: buttonBlocks,\n\t\t\tlayout: layout,\n\t\t},\n\t];\n};\n\n/**\n * core/cover → cover block\n */\nexport const cover: BlockTransformer = (block, _options, context) => {\n\tconst url = attrString(block.attrs, \"url\");\n\tconst overlayColor = attrString(block.attrs, \"overlayColor\");\n\tconst customOverlayColor = attrString(block.attrs, \"customOverlayColor\");\n\tconst dimRatio = attrNumber(block.attrs, \"dimRatio\");\n\tconst minHeight = attrNumber(block.attrs, \"minHeight\");\n\tconst minHeightUnit = attrString(block.attrs, \"minHeightUnit\");\n\tconst contentPosition = attrString(block.attrs, \"contentPosition\");\n\n\t// Transform inner blocks for content\n\tconst content = context.transformBlocks(block.innerBlocks);\n\n\t// Determine alignment from content position\n\tlet alignment: \"left\" | \"center\" | \"right\" | undefined;\n\tif (contentPosition?.includes(\"left\")) alignment = \"left\";\n\telse if (contentPosition?.includes(\"right\")) alignment = \"right\";\n\telse if (contentPosition?.includes(\"center\")) alignment = \"center\";\n\n\t// Build min height string\n\tlet minHeightStr: string | undefined;\n\tif (minHeight !== undefined) {\n\t\tminHeightStr = minHeightUnit ? `${minHeight}${minHeightUnit}` : `${minHeight}px`;\n\t}\n\n\treturn [\n\t\t{\n\t\t\t_type: \"cover\",\n\t\t\t_key: context.generateKey(),\n\t\t\tbackgroundImage: url,\n\t\t\toverlayColor: customOverlayColor || overlayColor,\n\t\t\toverlayOpacity: dimRatio !== undefined ? dimRatio / 100 : undefined,\n\t\t\tcontent,\n\t\t\tminHeight: minHeightStr,\n\t\t\talignment,\n\t\t},\n\t];\n};\n\n/**\n * core/file → file block\n */\nexport const file: BlockTransformer = (block, _options, context) => {\n\tconst href = sanitizeHref(attrString(block.attrs, \"href\"));\n\tconst fileName = attrString(block.attrs, \"fileName\");\n\tconst showDownloadButton = attrBoolean(block.attrs, \"showDownloadButton\");\n\n\t// Try to extract href from HTML if not in attrs\n\tlet url = href;\n\tif (!url) {\n\t\tconst hrefMatch = block.innerHTML.match(HREF_PATTERN);\n\t\turl = sanitizeHref(hrefMatch?.[1]);\n\t}\n\n\t// Try to extract filename from HTML if not in attrs\n\tlet filename = fileName;\n\tif (!filename && url) {\n\t\tfilename = url.split(\"/\").pop()?.split(\"?\")[0];\n\t}\n\n\treturn [\n\t\t{\n\t\t\t_type: \"file\",\n\t\t\t_key: context.generateKey(),\n\t\t\turl: url || \"\",\n\t\t\tfilename,\n\t\t\tshowDownloadButton: showDownloadButton !== false,\n\t\t},\n\t];\n};\n\n/**\n * core/pullquote → pullquote block\n */\nexport const pullquote: BlockTransformer = (block, _options, context) => {\n\t// Extract text from blockquote > p\n\tconst pMatch = block.innerHTML.match(P_TAG_SINGLE_PATTERN);\n\tconst text = pMatch ? extractText(pMatch[1]!) : extractText(block.innerHTML);\n\n\t// Extract citation\n\tconst citeMatch = block.innerHTML.match(CITE_TAG_PATTERN);\n\tconst citation = citeMatch ? extractText(citeMatch[1]!) : attrString(block.attrs, \"citation\");\n\n\treturn [\n\t\t{\n\t\t\t_type: \"pullquote\",\n\t\t\t_key: context.generateKey(),\n\t\t\ttext: text.trim(),\n\t\t\tcitation: citation?.trim(),\n\t\t},\n\t];\n};\n\n/**\n * core/html → htmlBlock (pass through)\n */\nexport const html: BlockTransformer = (block, _options, context) => {\n\treturn [\n\t\t{\n\t\t\t_type: \"htmlBlock\",\n\t\t\t_key: context.generateKey(),\n\t\t\thtml: block.innerHTML.trim(),\n\t\t\toriginalBlockName: \"core/html\",\n\t\t},\n\t];\n};\n\n/**\n * core/verse → code block (preserves whitespace like preformatted)\n */\nexport const verse: BlockTransformer = (block, _options, context) => {\n\tconst text = extractText(block.innerHTML);\n\n\treturn [\n\t\t{\n\t\t\t_type: \"code\",\n\t\t\t_key: context.generateKey(),\n\t\t\tcode: text,\n\t\t\tlanguage: \"text\", // Mark as plain text\n\t\t},\n\t];\n};\n\n/**\n * core/more → break block with \"readMore\" style\n */\nexport const more: BlockTransformer = (_block, _options, context) => {\n\treturn [\n\t\t{\n\t\t\t_type: \"break\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"lineBreak\", // Could be \"readMore\" if we add that type\n\t\t},\n\t];\n};\n\n/**\n * core/nextpage → break block with page break indicator\n */\nexport const nextpage: BlockTransformer = (_block, _options, context) => {\n\treturn [\n\t\t{\n\t\t\t_type: \"break\",\n\t\t\t_key: context.generateKey(),\n\t\t\tstyle: \"lineBreak\", // Could be \"pageBreak\" if we add that type\n\t\t},\n\t];\n};\n\n/**\n * core/shortcode → htmlBlock (preserve for manual handling)\n */\nexport const shortcode: BlockTransformer = (block, _options, context) => {\n\treturn [\n\t\t{\n\t\t\t_type: \"htmlBlock\",\n\t\t\t_key: context.generateKey(),\n\t\t\thtml: block.innerHTML.trim(),\n\t\t\toriginalBlockName: \"core/shortcode\",\n\t\t},\n\t];\n};\n\n/**\n * core/media-text → columns block with 2 columns\n */\nexport const mediaText: BlockTransformer = (block, _options, context) => {\n\tconst mediaId = attrNumber(block.attrs, \"mediaId\");\n\tconst mediaUrl = attrString(block.attrs, \"mediaUrl\");\n\tconst mediaType = attrString(block.attrs, \"mediaType\");\n\tconst mediaPosition = attrString(block.attrs, \"mediaPosition\");\n\tconst mediaAlt = attrString(block.attrs, \"mediaAlt\");\n\n\t// Create media column\n\tconst mediaBlock: PortableTextBlock[] =\n\t\tmediaType === \"video\"\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\t_type: \"embed\",\n\t\t\t\t\t\t_key: context.generateKey(),\n\t\t\t\t\t\turl: mediaUrl || \"\",\n\t\t\t\t\t\tprovider: \"video\",\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: [\n\t\t\t\t\t{\n\t\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t\t_key: context.generateKey(),\n\t\t\t\t\t\tasset: {\n\t\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t\t_ref: String(mediaId || mediaUrl || \"\"),\n\t\t\t\t\t\t\turl: mediaUrl,\n\t\t\t\t\t\t},\n\t\t\t\t\t\talt: mediaAlt,\n\t\t\t\t\t},\n\t\t\t\t];\n\n\t// Transform content blocks\n\tconst contentBlocks = context.transformBlocks(block.innerBlocks);\n\n\t// Order based on media position\n\tconst mediaTextColumns =\n\t\tmediaPosition === \"right\"\n\t\t\t? [\n\t\t\t\t\t{ _type: \"column\" as const, _key: context.generateKey(), content: contentBlocks },\n\t\t\t\t\t{ _type: \"column\" as const, _key: context.generateKey(), content: mediaBlock },\n\t\t\t\t]\n\t\t\t: [\n\t\t\t\t\t{ _type: \"column\" as const, _key: context.generateKey(), content: mediaBlock },\n\t\t\t\t\t{ _type: \"column\" as const, _key: context.generateKey(), content: contentBlocks },\n\t\t\t\t];\n\n\treturn [\n\t\t{\n\t\t\t_type: \"columns\",\n\t\t\t_key: context.generateKey(),\n\t\t\tcolumns: mediaTextColumns,\n\t\t},\n\t];\n};\n","/**\n * Transformers for WordPress embed blocks\n */\n\nimport type { BlockTransformer } from \"../types.js\";\nimport { attrString } from \"../types.js\";\n\n// Regex patterns for embed parsing\nconst IFRAME_SRC_PATTERN = /<iframe[^>]+src=[\"']([^\"']+)[\"']/i;\nconst VIDEO_SRC_PATTERN = /<video[^>]+src=[\"']([^\"']+)[\"']/i;\nconst VIDEO_SOURCE_PATTERN = /<source[^>]+src=[\"']([^\"']+)[\"']/i;\nconst AUDIO_SRC_PATTERN = /<audio[^>]+src=[\"']([^\"']+)[\"']/i;\nconst AUDIO_SOURCE_PATTERN = /<source[^>]+src=[\"']([^\"']+)[\"']/i;\n\n/**\n * core/embed and variants → embed block\n */\nexport const embed: BlockTransformer = (block, _options, context) => {\n\tconst url = attrString(block.attrs, \"url\");\n\tconst providerSlug = attrString(block.attrs, \"providerNameSlug\");\n\n\t// Extract iframe src if present\n\tconst iframeMatch = block.innerHTML.match(IFRAME_SRC_PATTERN);\n\tconst iframeSrc = iframeMatch?.[1];\n\n\treturn [\n\t\t{\n\t\t\t_type: \"embed\",\n\t\t\t_key: context.generateKey(),\n\t\t\turl: url || iframeSrc || \"\",\n\t\t\tprovider: providerSlug || detectProvider(url || iframeSrc || \"\"),\n\t\t\thtml: block.innerHTML.trim() || undefined,\n\t\t},\n\t];\n};\n\n/**\n * core-embed/youtube → embed block\n */\nexport const youtube: BlockTransformer = (block, options, context) => {\n\treturn embed(block, options, context);\n};\n\n/**\n * core-embed/twitter → embed block\n */\nexport const twitter: BlockTransformer = (block, options, context) => {\n\treturn embed(block, options, context);\n};\n\n/**\n * core-embed/vimeo → embed block\n */\nexport const vimeo: BlockTransformer = (block, options, context) => {\n\treturn embed(block, options, context);\n};\n\n/**\n * core/video → embed block (self-hosted video)\n */\nexport const video: BlockTransformer = (block, _options, context) => {\n\tconst src = attrString(block.attrs, \"src\");\n\n\t// Extract from video tag if not in attrs\n\tconst videoMatch = block.innerHTML.match(VIDEO_SRC_PATTERN);\n\tconst sourceMatch = block.innerHTML.match(VIDEO_SOURCE_PATTERN);\n\tconst videoSrc = src || videoMatch?.[1] || sourceMatch?.[1];\n\n\treturn [\n\t\t{\n\t\t\t_type: \"embed\",\n\t\t\t_key: context.generateKey(),\n\t\t\turl: videoSrc || \"\",\n\t\t\tprovider: \"video\",\n\t\t\thtml: block.innerHTML.trim() || undefined,\n\t\t},\n\t];\n};\n\n/**\n * core/audio → embed block (self-hosted audio)\n */\nexport const audio: BlockTransformer = (block, _options, context) => {\n\tconst src = attrString(block.attrs, \"src\");\n\n\t// Extract from audio tag if not in attrs\n\tconst audioMatch = block.innerHTML.match(AUDIO_SRC_PATTERN);\n\tconst sourceMatch = block.innerHTML.match(AUDIO_SOURCE_PATTERN);\n\tconst audioSrc = src || audioMatch?.[1] || sourceMatch?.[1];\n\n\treturn [\n\t\t{\n\t\t\t_type: \"embed\",\n\t\t\t_key: context.generateKey(),\n\t\t\turl: audioSrc || \"\",\n\t\t\tprovider: \"audio\",\n\t\t\thtml: block.innerHTML.trim() || undefined,\n\t\t},\n\t];\n};\n\n/**\n * Detect embed provider from URL\n */\nfunction detectProvider(url: string): string | undefined {\n\tif (!url) return undefined;\n\n\tconst urlLower = url.toLowerCase();\n\n\tif (urlLower.includes(\"youtube.com\") || urlLower.includes(\"youtu.be\")) {\n\t\treturn \"youtube\";\n\t}\n\tif (urlLower.includes(\"vimeo.com\")) {\n\t\treturn \"vimeo\";\n\t}\n\tif (urlLower.includes(\"twitter.com\") || urlLower.includes(\"x.com\")) {\n\t\treturn \"twitter\";\n\t}\n\tif (urlLower.includes(\"instagram.com\")) {\n\t\treturn \"instagram\";\n\t}\n\tif (urlLower.includes(\"facebook.com\")) {\n\t\treturn \"facebook\";\n\t}\n\tif (urlLower.includes(\"tiktok.com\")) {\n\t\treturn \"tiktok\";\n\t}\n\tif (urlLower.includes(\"spotify.com\")) {\n\t\treturn \"spotify\";\n\t}\n\tif (urlLower.includes(\"soundcloud.com\")) {\n\t\treturn \"soundcloud\";\n\t}\n\tif (urlLower.includes(\"codepen.io\")) {\n\t\treturn \"codepen\";\n\t}\n\tif (urlLower.includes(\"gist.github.com\")) {\n\t\treturn \"gist\";\n\t}\n\n\treturn undefined;\n}\n","/**\n * Block transformers registry\n */\n\nimport type { BlockTransformer, PortableTextBlock } from \"../types.js\";\nimport * as core from \"./core.js\";\nimport * as embed from \"./embed.js\";\n\n/**\n * Default block transformers for core WordPress blocks\n */\nexport const defaultTransformers: Record<string, BlockTransformer> = {\n\t// Text blocks\n\t\"core/paragraph\": core.paragraph,\n\t\"core/heading\": core.heading,\n\t\"core/list\": core.list,\n\t\"core/quote\": core.quote,\n\t\"core/code\": core.code,\n\t\"core/preformatted\": core.preformatted,\n\t\"core/pullquote\": core.pullquote,\n\t\"core/verse\": core.verse,\n\n\t// Media blocks\n\t\"core/image\": core.image,\n\t\"core/gallery\": core.gallery,\n\t\"core/file\": core.file,\n\t\"core/media-text\": core.mediaText,\n\t\"core/cover\": core.cover,\n\n\t// Layout blocks\n\t\"core/columns\": core.columns,\n\t\"core/group\": core.group,\n\t\"core/separator\": core.separator,\n\t\"core/spacer\": core.separator,\n\t\"core/table\": core.table,\n\t\"core/buttons\": core.buttons,\n\t\"core/button\": core.button,\n\n\t// Structural blocks\n\t\"core/more\": core.more,\n\t\"core/nextpage\": core.nextpage,\n\n\t// Pass-through blocks (preserve as HTML)\n\t\"core/html\": core.html,\n\t\"core/shortcode\": core.shortcode,\n\n\t// Embed blocks\n\t\"core/embed\": embed.embed,\n\t\"core/video\": embed.video,\n\t\"core/audio\": embed.audio,\n\n\t// Legacy embed block names (WP < 5.6)\n\t\"core-embed/youtube\": embed.youtube,\n\t\"core-embed/twitter\": embed.twitter,\n\t\"core-embed/vimeo\": embed.vimeo,\n\t\"core-embed/facebook\": embed.embed,\n\t\"core-embed/instagram\": embed.embed,\n\t\"core-embed/soundcloud\": embed.embed,\n\t\"core-embed/spotify\": embed.embed,\n};\n\n/**\n * Fallback transformer for unknown blocks\n * Stores the original HTML for manual review\n */\nexport const fallbackTransformer: BlockTransformer = (\n\tblock,\n\t_options,\n\tcontext,\n): PortableTextBlock[] => {\n\t// Skip completely empty blocks\n\tif (!block.innerHTML.trim() && block.innerBlocks.length === 0) {\n\t\treturn [];\n\t}\n\n\t// If it has inner blocks, try to transform those\n\tif (block.innerBlocks.length > 0) {\n\t\treturn context.transformBlocks(block.innerBlocks);\n\t}\n\n\t// Store as HTML fallback\n\treturn [\n\t\t{\n\t\t\t_type: \"htmlBlock\",\n\t\t\t_key: context.generateKey(),\n\t\t\thtml: block.innerHTML,\n\t\t\toriginalBlockName: block.blockName,\n\t\t\toriginalAttrs: Object.keys(block.attrs).length > 0 ? block.attrs : undefined,\n\t\t},\n\t];\n};\n\n/**\n * Get transformer for a block\n */\nexport function getTransformer(\n\tblockName: string | null,\n\tcustomTransformers?: Record<string, BlockTransformer>,\n): BlockTransformer {\n\tif (!blockName) {\n\t\treturn fallbackTransformer;\n\t}\n\n\t// Check custom transformers first\n\tif (customTransformers?.[blockName]) {\n\t\treturn customTransformers[blockName];\n\t}\n\n\t// Check default transformers\n\tif (defaultTransformers[blockName]) {\n\t\treturn defaultTransformers[blockName];\n\t}\n\n\treturn fallbackTransformer;\n}\n","/**\n * Gutenberg to Portable Text Converter\n *\n * Converts WordPress Gutenberg block content to Portable Text format.\n * Uses @wordpress/block-serialization-default-parser to parse the hybrid\n * HTML+JSON format that WordPress uses.\n */\n\nimport { parse } from \"@wordpress/block-serialization-default-parser\";\n\nimport { parseInlineContent } from \"./inline.js\";\nimport { getTransformer } from \"./transformers/index.js\";\nimport type {\n\tGutenbergBlock,\n\tPortableTextBlock,\n\tConvertOptions,\n\tTransformContext,\n} from \"./types.js\";\n\n// Regex patterns for HTML parsing and conversion\nconst BLOCK_ELEMENT_PATTERN =\n\t/<(p|h[1-6]|blockquote|pre|ul|ol|figure|div|hr)[^>]*>([\\s\\S]*?)<\\/\\1>|<(hr|br)\\s*\\/?>|<img\\s+[^>]+\\/?>/gu;\nconst LINKED_IMAGE_PATTERN = /<a\\s+[^>]*href=[\"']([^\"']+)[\"'][^>]*>\\s*<img\\s+([^>]+)\\/?>\\s*<\\/a>/gu;\nconst STANDALONE_IMAGE_PATTERN = /<img\\s+[^>]+\\/?>/gu;\nconst IMG_TAG_PATTERN = /<img[^>]+>/i;\nconst SRC_ATTR_PATTERN = /src=[\"']([^\"']+)[\"']/i;\nconst ALT_ATTR_PATTERN = /alt=[\"']([^\"']*)[\"']/i;\nconst LIST_ITEM_PATTERN = /<li[^>]*>([\\s\\S]*?)<\\/li>/gu;\nconst CODE_TAG_PATTERN = /<code[^>]*>([\\s\\S]*?)<\\/code>/i;\nconst HTML_TAG_PATTERN = /<[^>]+>/g;\nconst FIGCAPTION_TAG_PATTERN = /<figcaption[^>]*>([\\s\\S]*?)<\\/figcaption>/i;\nconst AMP_ENTITY_PATTERN = /&amp;/g;\nconst LESS_THAN_ENTITY_PATTERN = /&lt;/g;\nconst GREATER_THAN_ENTITY_PATTERN = /&gt;/g;\nconst QUOTE_ENTITY_PATTERN = /&quot;/g;\nconst APOS_ENTITY_PATTERN = /&#039;/g;\nconst NUMERIC_AMP_ENTITY_PATTERN = /&#0?38;/g;\nconst HEX_AMP_ENTITY_PATTERN = /&#x26;/gi;\nconst NBSP_ENTITY_PATTERN = /&nbsp;/g;\n\n// Re-export types\nexport type {\n\tGutenbergBlock,\n\tPortableTextBlock,\n\tPortableTextTextBlock,\n\tPortableTextImageBlock,\n\tPortableTextCodeBlock,\n\tPortableTextEmbedBlock,\n\tPortableTextGalleryBlock,\n\tPortableTextColumnsBlock,\n\tPortableTextBreakBlock,\n\tPortableTextHtmlBlock,\n\tPortableTextButtonBlock,\n\tPortableTextButtonsBlock,\n\tPortableTextCoverBlock,\n\tPortableTextFileBlock,\n\tPortableTextPullquoteBlock,\n\tPortableTextSpan,\n\tPortableTextMarkDef,\n\tConvertOptions,\n\tBlockTransformer,\n\tTransformContext,\n} from \"./types.js\";\n\n// Re-export transformers for customization\nexport { defaultTransformers, fallbackTransformer } from \"./transformers/index.js\";\nexport * as coreTransformers from \"./transformers/core.js\";\nexport * as embedTransformers from \"./transformers/embed.js\";\n\n// Re-export inline utilities\nexport {\n\tparseInlineContent,\n\textractText,\n\textractAlt,\n\textractCaption,\n\textractSrc,\n} from \"./inline.js\";\n\n/**\n * Default key generator\n */\nfunction createKeyGenerator(): () => string {\n\tlet counter = 0;\n\treturn () => {\n\t\tcounter++;\n\t\treturn `key-${counter}-${Math.random().toString(36).substring(2, 7)}`;\n\t};\n}\n\n/**\n * Normalize parsed blocks from the WP parser into our GutenbergBlock type.\n * The WP parser returns `attrs: Record<string, any> | null`, so we normalize\n * null attrs to empty objects and recursively process innerBlocks.\n */\nfunction normalizeBlocks(blocks: ReturnType<typeof parse>): GutenbergBlock[] {\n\treturn blocks.map(\n\t\t(block): GutenbergBlock => ({\n\t\t\tblockName: block.blockName,\n\t\t\tattrs: (block.attrs ?? {}) satisfies Record<string, unknown>,\n\t\t\tinnerHTML: block.innerHTML,\n\t\t\tinnerBlocks: normalizeBlocks(block.innerBlocks),\n\t\t\tinnerContent: block.innerContent,\n\t\t}),\n\t);\n}\n\n/**\n * Convert WordPress Gutenberg content to Portable Text\n *\n * @param content - WordPress post content (HTML with Gutenberg block comments)\n * @param options - Conversion options\n * @returns Array of Portable Text blocks\n *\n * @example\n * ```ts\n * const portableText = gutenbergToPortableText(`\n * <!-- wp:paragraph -->\n * <p>Hello <strong>world</strong>!</p>\n * <!-- /wp:paragraph -->\n * `);\n * // → [{ _type: \"block\", style: \"normal\", children: [...] }]\n * ```\n */\nexport function gutenbergToPortableText(\n\tcontent: string,\n\toptions: ConvertOptions = {},\n): PortableTextBlock[] {\n\t// Handle empty content\n\tif (!content || !content.trim()) {\n\t\treturn [];\n\t}\n\n\t// Check if content has Gutenberg blocks\n\tconst hasBlocks = content.includes(\"<!-- wp:\");\n\n\tif (!hasBlocks) {\n\t\t// Classic editor content - treat as HTML\n\t\treturn htmlToPortableText(content, options);\n\t}\n\n\t// Parse Gutenberg blocks\n\tconst blocks = normalizeBlocks(parse(content));\n\n\t// Create key generator\n\tconst generateKey = options.keyGenerator || createKeyGenerator();\n\n\t// Create transform context\n\tconst context = createTransformContext(options, generateKey);\n\n\t// Transform blocks\n\treturn blocks.flatMap((block) => transformBlock(block, options, context));\n}\n\n/**\n * Convert plain HTML (classic editor) to Portable Text\n */\nexport function htmlToPortableText(\n\thtml: string,\n\toptions: ConvertOptions = {},\n): PortableTextBlock[] {\n\tconst generateKey = options.keyGenerator || createKeyGenerator();\n\tconst blocks: PortableTextBlock[] = [];\n\n\t// Split on block-level elements (including standalone img tags)\n\tlet lastIndex = 0;\n\tlet match;\n\n\twhile ((match = BLOCK_ELEMENT_PATTERN.exec(html)) !== null) {\n\t\tconst fullMatch = match[0];\n\t\tconst tag = (match[1] || match[3] || \"\").toLowerCase();\n\t\tconst content = match[2] || \"\";\n\n\t\t// Handle text between matches\n\t\tconst between = html.slice(lastIndex, match.index).trim();\n\t\tif (between) {\n\t\t\tconst { children, markDefs } = parseInlineContent(between, generateKey);\n\t\t\tif (children.some((c) => c.text.trim())) {\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tstyle: \"normal\",\n\t\t\t\t\tchildren,\n\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tlastIndex = match.index + match[0].length;\n\n\t\t// Check for standalone <img> tag (not wrapped in figure/p)\n\t\tif (fullMatch.toLowerCase().startsWith(\"<img\")) {\n\t\t\tconst srcMatch = fullMatch.match(SRC_ATTR_PATTERN);\n\t\t\tconst altMatch = fullMatch.match(ALT_ATTR_PATTERN);\n\t\t\tif (srcMatch?.[1]) {\n\t\t\t\tconst imgUrl = decodeUrlEntities(srcMatch[1]);\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tasset: {\n\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t_ref: imgUrl,\n\t\t\t\t\t\turl: imgUrl,\n\t\t\t\t\t},\n\t\t\t\t\talt: altMatch?.[1],\n\t\t\t\t});\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Transform based on tag\n\t\tswitch (tag) {\n\t\t\tcase \"p\":\n\t\t\tcase \"div\": {\n\t\t\t\t// Extract any images first (including those wrapped in <a> tags)\n\t\t\t\t// Match: <a...><img...></a> or standalone <img...>\n\t\t\t\t// Track positions of linked images so we don't double-process\n\t\t\t\tconst linkedImgPositions: Array<{ start: number; end: number }> = [];\n\n\t\t\t\t// First extract linked images\n\t\t\t\tlet linkedMatch;\n\t\t\t\twhile ((linkedMatch = LINKED_IMAGE_PATTERN.exec(content)) !== null) {\n\t\t\t\t\tconst linkUrl = decodeUrlEntities(linkedMatch[1]!);\n\t\t\t\t\tconst imgAttrs = linkedMatch[2]!;\n\t\t\t\t\tconst srcMatch = imgAttrs.match(SRC_ATTR_PATTERN);\n\t\t\t\t\tconst altMatch = imgAttrs.match(ALT_ATTR_PATTERN);\n\t\t\t\t\tif (srcMatch?.[1]) {\n\t\t\t\t\t\tconst imgUrl = decodeUrlEntities(srcMatch[1]);\n\t\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\tasset: {\n\t\t\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t\t\t_ref: imgUrl,\n\t\t\t\t\t\t\t\turl: imgUrl,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\talt: altMatch?.[1],\n\t\t\t\t\t\t\tlink: linkUrl,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tlinkedImgPositions.push({\n\t\t\t\t\t\tstart: linkedMatch.index,\n\t\t\t\t\t\tend: linkedMatch.index + linkedMatch[0].length,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Then extract standalone images (not inside <a> tags)\n\t\t\t\tlet imgMatch;\n\t\t\t\twhile ((imgMatch = STANDALONE_IMAGE_PATTERN.exec(content)) !== null) {\n\t\t\t\t\t// Skip if this image is inside a linked image we already processed\n\t\t\t\t\tconst isLinked = linkedImgPositions.some(\n\t\t\t\t\t\t(pos) => imgMatch!.index >= pos.start && imgMatch!.index < pos.end,\n\t\t\t\t\t);\n\t\t\t\t\tif (isLinked) continue;\n\n\t\t\t\t\tconst srcMatch = imgMatch[0].match(SRC_ATTR_PATTERN);\n\t\t\t\t\tconst altMatch = imgMatch[0].match(ALT_ATTR_PATTERN);\n\t\t\t\t\tif (srcMatch?.[1]) {\n\t\t\t\t\t\tconst imgUrl = decodeUrlEntities(srcMatch[1]);\n\t\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\tasset: {\n\t\t\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t\t\t_ref: imgUrl,\n\t\t\t\t\t\t\t\turl: imgUrl,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\talt: altMatch?.[1],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Then handle the text content (with images and image links stripped)\n\t\t\t\tlet textContent = content\n\t\t\t\t\t.replace(LINKED_IMAGE_PATTERN, \"\") // Remove linked images\n\t\t\t\t\t.replace(STANDALONE_IMAGE_PATTERN, \"\") // Remove standalone images\n\t\t\t\t\t.trim();\n\t\t\t\tif (textContent) {\n\t\t\t\t\tconst { children, markDefs } = parseInlineContent(textContent, generateKey);\n\t\t\t\t\tif (children.some((c) => c.text.trim())) {\n\t\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\tstyle: \"normal\",\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"h1\":\n\t\t\tcase \"h2\":\n\t\t\tcase \"h3\":\n\t\t\tcase \"h4\":\n\t\t\tcase \"h5\":\n\t\t\tcase \"h6\": {\n\t\t\t\tconst { children, markDefs } = parseInlineContent(content, generateKey);\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tstyle: tag,\n\t\t\t\t\tchildren,\n\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"blockquote\": {\n\t\t\t\tconst { children, markDefs } = parseInlineContent(content, generateKey);\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tstyle: \"blockquote\",\n\t\t\t\t\tchildren,\n\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"pre\": {\n\t\t\t\t// Extract code content\n\t\t\t\tconst codeMatch = content.match(CODE_TAG_PATTERN);\n\t\t\t\tconst code = codeMatch?.[1] || content;\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"code\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tcode: decodeHtmlEntities(code),\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"ul\":\n\t\t\tcase \"ol\": {\n\t\t\t\tconst listItem = tag === \"ol\" ? \"number\" : \"bullet\";\n\t\t\t\tlet liMatch;\n\t\t\t\twhile ((liMatch = LIST_ITEM_PATTERN.exec(content)) !== null) {\n\t\t\t\t\tconst liContent = liMatch[1] || \"\";\n\t\t\t\t\tconst { children, markDefs } = parseInlineContent(liContent, generateKey);\n\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\tstyle: \"normal\",\n\t\t\t\t\t\tlistItem,\n\t\t\t\t\t\tlevel: 1,\n\t\t\t\t\t\tchildren,\n\t\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"hr\": {\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"break\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tstyle: \"lineBreak\",\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"figure\": {\n\t\t\t\t// Check for image\n\t\t\t\tconst imgMatch = content.match(IMG_TAG_PATTERN);\n\t\t\t\tif (imgMatch) {\n\t\t\t\t\tconst srcMatch = imgMatch[0].match(SRC_ATTR_PATTERN);\n\t\t\t\t\tconst altMatch = imgMatch[0].match(ALT_ATTR_PATTERN);\n\t\t\t\t\tconst captionMatch = content.match(FIGCAPTION_TAG_PATTERN);\n\t\t\t\t\tconst imgUrl = srcMatch?.[1] ? decodeUrlEntities(srcMatch[1]) : \"\";\n\n\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t_type: \"image\",\n\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\tasset: {\n\t\t\t\t\t\t\t_type: \"reference\",\n\t\t\t\t\t\t\t_ref: imgUrl,\n\t\t\t\t\t\t\turl: imgUrl || undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t\talt: altMatch?.[1],\n\t\t\t\t\t\tcaption: captionMatch?.[1]?.replace(HTML_TAG_PATTERN, \"\").trim(),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle remaining text\n\tconst remaining = html.slice(lastIndex).trim();\n\tif (remaining) {\n\t\tconst { children, markDefs } = parseInlineContent(remaining, generateKey);\n\t\tif (children.some((c) => c.text.trim())) {\n\t\t\tblocks.push({\n\t\t\t\t_type: \"block\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\tstyle: \"normal\",\n\t\t\t\tchildren,\n\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Create transform context for recursive block transformation\n */\nfunction createTransformContext(\n\toptions: ConvertOptions,\n\tgenerateKey: () => string,\n): TransformContext {\n\tconst context: TransformContext = {\n\t\tgenerateKey,\n\t\tparseInlineContent: (html: string) => parseInlineContent(html, generateKey),\n\t\ttransformBlocks: (blocks: GutenbergBlock[]) =>\n\t\t\tblocks.flatMap((block) => transformBlock(block, options, context)),\n\t};\n\treturn context;\n}\n\n/**\n * Transform a single block\n */\nfunction transformBlock(\n\tblock: GutenbergBlock,\n\toptions: ConvertOptions,\n\tcontext: TransformContext,\n): PortableTextBlock[] {\n\tconst transformer = getTransformer(block.blockName, options.customTransformers);\n\treturn transformer(block, options, context);\n}\n\n/**\n * Decode HTML entities\n */\nfunction decodeHtmlEntities(html: string): string {\n\treturn html\n\t\t.replace(LESS_THAN_ENTITY_PATTERN, \"<\")\n\t\t.replace(GREATER_THAN_ENTITY_PATTERN, \">\")\n\t\t.replace(AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(QUOTE_ENTITY_PATTERN, '\"')\n\t\t.replace(APOS_ENTITY_PATTERN, \"'\")\n\t\t.replace(NUMERIC_AMP_ENTITY_PATTERN, \"&\") // &#038; or &#38;\n\t\t.replace(HEX_AMP_ENTITY_PATTERN, \"&\") // &#x26;\n\t\t.replace(NBSP_ENTITY_PATTERN, \" \");\n}\n\n/**\n * Decode HTML entities in URLs (used for image src attributes)\n */\nfunction decodeUrlEntities(url: string): string {\n\treturn url\n\t\t.replace(AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(NUMERIC_AMP_ENTITY_PATTERN, \"&\")\n\t\t.replace(HEX_AMP_ENTITY_PATTERN, \"&\");\n}\n\n/**\n * Parse Gutenberg blocks without converting to Portable Text\n * Useful for inspection and debugging\n */\nexport function parseGutenbergBlocks(content: string): GutenbergBlock[] {\n\tif (!content || !content.trim()) {\n\t\treturn [];\n\t}\n\treturn normalizeBlocks(parse(content));\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emdash-cms/gutenberg-to-portable-text",
3
- "version": "0.0.1",
3
+ "version": "0.2.0",
4
4
  "description": "Convert WordPress Gutenberg blocks to Portable Text",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -27,10 +27,10 @@
27
27
  },
28
28
  "repository": {
29
29
  "type": "git",
30
- "url": "git+https://github.com/cloudflare/emdash.git",
30
+ "url": "git+https://github.com/emdash-cms/emdash.git",
31
31
  "directory": "packages/gutenberg-to-portable-text"
32
32
  },
33
- "homepage": "https://github.com/cloudflare/emdash",
33
+ "homepage": "https://github.com/emdash-cms/emdash",
34
34
  "keywords": [
35
35
  "wordpress",
36
36
  "gutenberg",