@hirokisakabe/pom 8.2.1 → 8.4.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.
Files changed (136) hide show
  1. package/README.md +37 -25
  2. package/dist/autoFit/autoFit.js +1 -1
  3. package/dist/autoFit/autoFit.js.map +1 -1
  4. package/dist/autoFit/strategies/reduceFontSize.js +16 -14
  5. package/dist/autoFit/strategies/reduceFontSize.js.map +1 -1
  6. package/dist/autoFit/strategies/reduceGapAndPadding.js +13 -20
  7. package/dist/autoFit/strategies/reduceGapAndPadding.js.map +1 -1
  8. package/dist/autoFit/strategies/reduceTableRowHeight.js +8 -2
  9. package/dist/autoFit/strategies/reduceTableRowHeight.js.map +1 -1
  10. package/dist/autoFit/strategies/uniformScale.js +19 -20
  11. package/dist/autoFit/strategies/uniformScale.js.map +1 -1
  12. package/dist/autoFit/strategyResult.js +15 -0
  13. package/dist/autoFit/strategyResult.js.map +1 -0
  14. package/dist/buildContext.js +3 -1
  15. package/dist/buildContext.js.map +1 -1
  16. package/dist/buildPptx.d.ts.map +1 -1
  17. package/dist/buildPptx.js +5 -1
  18. package/dist/buildPptx.js.map +1 -1
  19. package/dist/calcYogaLayout/calcYogaLayout.js +18 -28
  20. package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
  21. package/dist/calcYogaLayout/fontLoader.js.map +1 -1
  22. package/dist/calcYogaLayout/measureText.d.ts.map +1 -1
  23. package/dist/calcYogaLayout/measureText.js +9 -2
  24. package/dist/calcYogaLayout/measureText.js.map +1 -1
  25. package/dist/diagnostics.d.ts +1 -1
  26. package/dist/diagnostics.d.ts.map +1 -1
  27. package/dist/diagnostics.js.map +1 -1
  28. package/dist/icons/renderIcon.js.map +1 -1
  29. package/dist/parseMasterPptx.js.map +1 -1
  30. package/dist/parseXml/coercionRules.js +48 -9
  31. package/dist/parseXml/coercionRules.js.map +1 -1
  32. package/dist/parseXml/parseXml.d.ts +8 -3
  33. package/dist/parseXml/parseXml.d.ts.map +1 -1
  34. package/dist/parseXml/parseXml.js +192 -209
  35. package/dist/parseXml/parseXml.js.map +1 -1
  36. package/dist/parseXml/serializeXml.d.ts.map +1 -1
  37. package/dist/parseXml/serializeXml.js +13 -17
  38. package/dist/parseXml/serializeXml.js.map +1 -1
  39. package/dist/registry/definitions/arrow.js +2 -2
  40. package/dist/registry/definitions/arrow.js.map +1 -1
  41. package/dist/registry/definitions/chart.js +2 -2
  42. package/dist/registry/definitions/chart.js.map +1 -1
  43. package/dist/registry/definitions/compositeNodes.js +7 -12
  44. package/dist/registry/definitions/compositeNodes.js.map +1 -1
  45. package/dist/registry/definitions/icon.js +2 -2
  46. package/dist/registry/definitions/icon.js.map +1 -1
  47. package/dist/registry/definitions/image.js +2 -2
  48. package/dist/registry/definitions/image.js.map +1 -1
  49. package/dist/registry/definitions/layer.js +4 -5
  50. package/dist/registry/definitions/layer.js.map +1 -1
  51. package/dist/registry/definitions/line.js +2 -2
  52. package/dist/registry/definitions/line.js.map +1 -1
  53. package/dist/registry/definitions/list.js +3 -4
  54. package/dist/registry/definitions/list.js.map +1 -1
  55. package/dist/registry/definitions/shape.js +2 -2
  56. package/dist/registry/definitions/shape.js.map +1 -1
  57. package/dist/registry/definitions/stack.js +3 -4
  58. package/dist/registry/definitions/stack.js.map +1 -1
  59. package/dist/registry/definitions/svg.js +2 -2
  60. package/dist/registry/definitions/svg.js.map +1 -1
  61. package/dist/registry/definitions/table.js +2 -2
  62. package/dist/registry/definitions/table.js.map +1 -1
  63. package/dist/registry/definitions/text.js +5 -3
  64. package/dist/registry/definitions/text.js.map +1 -1
  65. package/dist/registry/index.js.map +1 -1
  66. package/dist/registry/nodeMetadata.js +208 -0
  67. package/dist/registry/nodeMetadata.js.map +1 -0
  68. package/dist/registry/nodeRegistry.js +3 -0
  69. package/dist/registry/nodeRegistry.js.map +1 -1
  70. package/dist/registry/xmlChildRules.js +55 -0
  71. package/dist/registry/xmlChildRules.js.map +1 -0
  72. package/dist/renderPptx/gradientFills.js +139 -0
  73. package/dist/renderPptx/gradientFills.js.map +1 -0
  74. package/dist/renderPptx/nodes/arrow.js +7 -28
  75. package/dist/renderPptx/nodes/arrow.js.map +1 -1
  76. package/dist/renderPptx/nodes/chart.js +2 -7
  77. package/dist/renderPptx/nodes/chart.js.map +1 -1
  78. package/dist/renderPptx/nodes/flow.js +6 -13
  79. package/dist/renderPptx/nodes/flow.js.map +1 -1
  80. package/dist/renderPptx/nodes/icon.js +4 -2
  81. package/dist/renderPptx/nodes/icon.js.map +1 -1
  82. package/dist/renderPptx/nodes/image.js +5 -13
  83. package/dist/renderPptx/nodes/image.js.map +1 -1
  84. package/dist/renderPptx/nodes/line.js +9 -33
  85. package/dist/renderPptx/nodes/line.js.map +1 -1
  86. package/dist/renderPptx/nodes/list.js +8 -20
  87. package/dist/renderPptx/nodes/list.js.map +1 -1
  88. package/dist/renderPptx/nodes/matrix.js +10 -11
  89. package/dist/renderPptx/nodes/matrix.js.map +1 -1
  90. package/dist/renderPptx/nodes/processArrow.js +9 -16
  91. package/dist/renderPptx/nodes/processArrow.js.map +1 -1
  92. package/dist/renderPptx/nodes/pyramid.js +5 -7
  93. package/dist/renderPptx/nodes/pyramid.js.map +1 -1
  94. package/dist/renderPptx/nodes/shape.js +7 -20
  95. package/dist/renderPptx/nodes/shape.js.map +1 -1
  96. package/dist/renderPptx/nodes/svg.js +2 -5
  97. package/dist/renderPptx/nodes/svg.js.map +1 -1
  98. package/dist/renderPptx/nodes/table.js +2 -5
  99. package/dist/renderPptx/nodes/table.js.map +1 -1
  100. package/dist/renderPptx/nodes/text.js +22 -15
  101. package/dist/renderPptx/nodes/text.js.map +1 -1
  102. package/dist/renderPptx/nodes/timeline.js +20 -22
  103. package/dist/renderPptx/nodes/timeline.js.map +1 -1
  104. package/dist/renderPptx/nodes/tree.js +5 -5
  105. package/dist/renderPptx/nodes/tree.js.map +1 -1
  106. package/dist/renderPptx/renderPptx.js +18 -30
  107. package/dist/renderPptx/renderPptx.js.map +1 -1
  108. package/dist/renderPptx/textOptions.js +34 -9
  109. package/dist/renderPptx/textOptions.js.map +1 -1
  110. package/dist/renderPptx/units.js +11 -1
  111. package/dist/renderPptx/units.js.map +1 -1
  112. package/dist/renderPptx/utils/backgroundBorder.js +107 -59
  113. package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
  114. package/dist/renderPptx/utils/contentArea.js +26 -9
  115. package/dist/renderPptx/utils/contentArea.js.map +1 -1
  116. package/dist/renderPptx/utils/scaleToFit.js +17 -1
  117. package/dist/renderPptx/utils/scaleToFit.js.map +1 -1
  118. package/dist/renderPptx/utils/straightLine.js +41 -0
  119. package/dist/renderPptx/utils/straightLine.js.map +1 -0
  120. package/dist/renderPptx/utils/visualStyle.js +113 -0
  121. package/dist/renderPptx/utils/visualStyle.js.map +1 -0
  122. package/dist/shared/boxSpacing.js +63 -0
  123. package/dist/shared/boxSpacing.js.map +1 -0
  124. package/dist/shared/gradient.js +103 -0
  125. package/dist/shared/gradient.js.map +1 -0
  126. package/dist/shared/measureImage.js.map +1 -1
  127. package/dist/shared/tableUtils.js.map +1 -1
  128. package/dist/shared/walkTree.js +1 -7
  129. package/dist/shared/walkTree.js.map +1 -1
  130. package/dist/toPositioned/toPositioned.js +1 -1
  131. package/dist/toPositioned/toPositioned.js.map +1 -1
  132. package/dist/types.d.ts +1166 -93
  133. package/dist/types.d.ts.map +1 -1
  134. package/dist/types.js +54 -18
  135. package/dist/types.js.map +1 -1
  136. package/package.json +10 -9
@@ -1 +1 @@
1
- {"version":3,"file":"parseXml.js","names":[],"sources":["../../src/parseXml/parseXml.ts"],"sourcesContent":["import { XMLBuilder, XMLParser } from \"fast-xml-parser\";\nimport { z } from \"zod\";\nimport {\n type POMNode,\n textNodeSchema,\n ulNodeSchema,\n olNodeSchema,\n imageNodeSchema,\n tableNodeSchema,\n shapeNodeSchema,\n chartNodeSchema,\n timelineNodeSchema,\n matrixNodeSchema,\n treeNodeSchema,\n flowNodeSchema,\n processArrowNodeSchema,\n pyramidNodeSchema,\n lineNodeSchema,\n arrowNodeSchema,\n iconNodeSchema,\n} from \"../types.ts\";\nimport {\n type CoercionRule,\n NODE_COERCION_MAP,\n CHILD_ELEMENT_COERCION_MAP,\n coerceWithRule,\n coerceFallback,\n getObjectShapeFromRule,\n resolveMixedNotationShorthand,\n} from \"./coercionRules.ts\";\n\n// ===== ParseXmlError =====\nexport class ParseXmlError extends Error {\n public readonly errors: string[];\n constructor(errors: string[]) {\n const message = `XML validation failed (${errors.length} error${errors.length > 1 ? \"s\" : \"\"}):\\n${errors.map((e) => ` - ${e}`).join(\"\\n\")}`;\n super(message);\n this.name = \"ParseXmlError\";\n this.errors = errors;\n }\n}\n\n// ===== Tag name → POM node type mapping =====\nexport const TAG_TO_TYPE: Record<string, string> = {\n Text: \"text\",\n Image: \"image\",\n Table: \"table\",\n Shape: \"shape\",\n Chart: \"chart\",\n Timeline: \"timeline\",\n Matrix: \"matrix\",\n Tree: \"tree\",\n Flow: \"flow\",\n ProcessArrow: \"processArrow\",\n Pyramid: \"pyramid\",\n Ul: \"ul\",\n Ol: \"ol\",\n Line: \"line\",\n Arrow: \"arrow\",\n VStack: \"vstack\",\n HStack: \"hstack\",\n Layer: \"layer\",\n Icon: \"icon\",\n Svg: \"svg\",\n};\n\n// Reverse mapping: node type → tag name\nconst TYPE_TO_TAG: Record<string, string> = Object.fromEntries(\n Object.entries(TAG_TO_TYPE).map(([tag, type]) => [type, tag]),\n);\n\nconst CONTAINER_TYPES = new Set([\"vstack\", \"hstack\", \"layer\"]);\nconst TEXT_CONTENT_NODES = new Set([\"text\", \"shape\"]);\n// Attributes allowed on any node (e.g., x/y for Layer children positioning)\nconst UNIVERSAL_ATTRS = new Set([\"x\", \"y\"]);\n\n// ===== Validation helpers =====\nfunction getKnownAttributes(nodeType: string): string[] {\n const rules = NODE_COERCION_MAP[nodeType];\n if (!rules) return [];\n return Object.keys(rules);\n}\n\nfunction levenshteinDistance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n const dp: number[][] = Array.from({ length: m + 1 }, () =>\n Array<number>(n + 1).fill(0),\n );\n for (let i = 0; i <= m; i++) dp[i][0] = i;\n for (let j = 0; j <= n; j++) dp[0][j] = j;\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i][j] =\n a[i - 1] === b[j - 1]\n ? dp[i - 1][j - 1]\n : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);\n }\n }\n return dp[m][n];\n}\n\nfunction findClosestMatch(\n input: string,\n candidates: string[],\n): string | undefined {\n const threshold = Math.max(2, Math.floor(input.length / 2));\n let bestMatch: string | undefined;\n let bestDistance = Infinity;\n for (const candidate of candidates) {\n const dist = levenshteinDistance(\n input.toLowerCase(),\n candidate.toLowerCase(),\n );\n if (dist < bestDistance && dist <= threshold) {\n bestDistance = dist;\n bestMatch = candidate;\n }\n }\n return bestMatch;\n}\n\nfunction getKnownChildAttributes(tagName: string): string[] {\n const rules = CHILD_ELEMENT_COERCION_MAP[tagName];\n if (!rules) return [];\n return Object.keys(rules);\n}\n\n// ===== Leaf node Zod validation schemas =====\nconst leafNodeValidationSchemas: Record<string, z.ZodTypeAny> = {\n text: textNodeSchema,\n image: imageNodeSchema,\n table: tableNodeSchema,\n shape: shapeNodeSchema,\n chart: chartNodeSchema,\n timeline: timelineNodeSchema,\n matrix: matrixNodeSchema,\n tree: treeNodeSchema,\n flow: flowNodeSchema,\n processArrow: processArrowNodeSchema,\n pyramid: pyramidNodeSchema,\n line: lineNodeSchema,\n arrow: arrowNodeSchema,\n ul: ulNodeSchema,\n ol: olNodeSchema,\n icon: iconNodeSchema,\n};\n\nfunction formatZodIssue(\n issue: z.core.$ZodIssue,\n tagName: string,\n): string | null {\n const path = issue.path;\n // Skip children-related issues (validated recursively)\n if (path.length > 0 && path[0] === \"children\") return null;\n // Skip \"type\" field issues (set internally)\n if (path.length === 1 && path[0] === \"type\") return null;\n\n const attrName = path.length > 0 ? String(path[0]) : undefined;\n\n const code = issue.code;\n\n if (code === \"invalid_type\") {\n // Missing required attribute\n if (issue.input === undefined) {\n if (attrName) {\n return `<${tagName}>: Missing required attribute \"${attrName}\"`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n // Type mismatch\n if (attrName) {\n return `<${tagName}>: Invalid type for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n if (code === \"invalid_value\") {\n if (attrName) {\n const values = (issue as unknown as { values: string[] }).values;\n if (values) {\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". Expected: ${values.map((v) => `\"${v}\"`).join(\", \")}`;\n }\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n if (code === \"too_small\" || code === \"too_big\") {\n if (attrName) {\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n // Generic fallback\n if (attrName) {\n return `<${tagName}>: Attribute \"${attrName}\": ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n}\n\n// Properties that may be legitimately absent when using child element notation\n// or when the property is optional in practice (even if required in schema).\nconst CHILD_ELEMENT_PROPS: Record<string, Set<string>> = {\n flow: new Set([\"nodes\", \"connections\"]),\n table: new Set([\"columns\", \"rows\"]),\n chart: new Set([\"data\"]),\n timeline: new Set([\"items\"]),\n matrix: new Set([\"axes\", \"items\", \"quadrants\"]),\n processArrow: new Set([\"steps\"]),\n pyramid: new Set([\"levels\"]),\n tree: new Set([\"data\"]),\n ul: new Set([\"items\"]),\n ol: new Set([\"items\"]),\n icon: new Set([\"name\"]),\n svg: new Set([\"svgContent\"]),\n};\n\nfunction validateLeafNode(\n nodeType: string,\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const schema = leafNodeValidationSchemas[nodeType];\n if (!schema) return;\n const tagName = TYPE_TO_TAG[nodeType] ?? nodeType;\n const childProps = CHILD_ELEMENT_PROPS[nodeType];\n const parseResult = schema.safeParse(result);\n if (!parseResult.success) {\n const seen = new Set<string>();\n for (const issue of parseResult.error.issues) {\n // Skip only top-level missing child-element properties (path.length === 1)\n // Nested issues (e.g., data.children[0].label) must still be reported\n if (\n childProps &&\n issue.path.length === 1 &&\n childProps.has(String(issue.path[0])) &&\n issue.code === \"invalid_type\" &&\n issue.input === undefined\n ) {\n continue;\n }\n // Skip issues for universal attributes (x, y)\n if (issue.path.length > 0 && UNIVERSAL_ATTRS.has(String(issue.path[0]))) {\n continue;\n }\n const msg = formatZodIssue(issue, tagName);\n if (msg && !seen.has(msg)) {\n seen.add(msg);\n errors.push(msg);\n }\n }\n }\n}\n\n// ===== Types for XML parser output (preserveOrder mode) =====\ntype XmlNode = XmlElement | XmlTextNode;\ntype XmlTextNode = { \"#text\": string };\ninterface XmlElement {\n [tagName: string]: XmlNode[] | Record<string, string> | undefined;\n \":@\"?: Record<string, string>;\n}\n\n// ===== Coercion rule lookup =====\n\nfunction getCoercionRule(\n nodeType: string,\n propertyName: string,\n): CoercionRule | undefined {\n return NODE_COERCION_MAP[nodeType]?.[propertyName];\n}\n\n// ===== Dot notation helpers =====\n\n// ===== Dot notation expansion =====\nfunction expandDotNotation(attrs: Record<string, string>): {\n regular: Record<string, string>;\n dotGroups: Record<string, Record<string, string>>;\n} {\n const regular: Record<string, string> = {};\n const dotGroups: Record<string, Record<string, string>> = {};\n\n for (const [key, value] of Object.entries(attrs)) {\n const dotIndex = key.indexOf(\".\");\n if (dotIndex > 0) {\n const prefix = key.substring(0, dotIndex);\n const suffix = key.substring(dotIndex + 1);\n if (!dotGroups[prefix]) dotGroups[prefix] = {};\n dotGroups[prefix][suffix] = value;\n } else {\n regular[key] = value;\n }\n }\n\n return { regular, dotGroups };\n}\n\nfunction coerceDotGroup(\n prefix: string,\n subAttrs: Record<string, string>,\n rule: CoercionRule,\n tagName: string,\n errors: string[],\n): Record<string, unknown> {\n const objectShape = getObjectShapeFromRule(rule);\n\n const obj: Record<string, unknown> = {};\n if (objectShape) {\n for (const [subKey, subValue] of Object.entries(subAttrs)) {\n if (objectShape[subKey]) {\n const coerced = coerceWithRule(subValue, objectShape[subKey]);\n if (coerced.error !== null) {\n errors.push(`<${tagName}>: ${prefix}.${subKey}: ${coerced.error}`);\n } else {\n obj[subKey] = coerced.value;\n }\n } else {\n const knownSubKeys = Object.keys(objectShape);\n const suggestion = findClosestMatch(subKey, knownSubKeys);\n errors.push(\n `<${tagName}>: Unknown sub-attribute \"${prefix}.${subKey}\"${suggestion ? `. Did you mean \"${prefix}.${suggestion}\"?` : \"\"}`,\n );\n }\n }\n } else {\n errors.push(\n `<${tagName}>: Attribute \"${prefix}\" does not support dot notation`,\n );\n }\n return obj;\n}\n\n// ===== XML node helpers =====\nfunction isTextNode(node: XmlNode): node is XmlTextNode {\n return \"#text\" in node;\n}\n\nfunction getTagName(node: XmlElement): string {\n for (const key of Object.keys(node)) {\n if (key !== \":@\") return key;\n }\n throw new Error(\"No tag name found in XML element\");\n}\n\nfunction getAttributes(node: XmlElement): Record<string, string> {\n const attrs: Record<string, string> = {};\n const rawAttrs = node[\":@\"];\n if (rawAttrs) {\n for (const [key, value] of Object.entries(rawAttrs)) {\n const attrName = key.startsWith(\"@_\") ? key.slice(2) : key;\n attrs[attrName] = value.trim();\n }\n }\n return attrs;\n}\n\nfunction getChildElements(node: XmlElement): XmlElement[] {\n const tagName = getTagName(node);\n const children = node[tagName] as XmlNode[] | undefined;\n if (!children) return [];\n return children.filter((child): child is XmlElement => !isTextNode(child));\n}\n\nfunction getTextContent(node: XmlElement): string | undefined {\n const tagName = getTagName(node);\n const children = node[tagName] as XmlNode[] | undefined;\n if (!children) return undefined;\n const textParts: string[] = [];\n for (const child of children) {\n if (isTextNode(child)) {\n textParts.push(child[\"#text\"]);\n }\n }\n return textParts.length > 0 ? textParts.join(\"\") : undefined;\n}\n\nfunction getRawChildren(node: XmlElement): XmlNode[] {\n const tagName = getTagName(node);\n return (node[tagName] as XmlNode[] | undefined) ?? [];\n}\n\nconst INLINE_FORMAT_TAGS = new Set([\"B\", \"I\", \"A\", \"U\", \"S\", \"Mark\", \"Span\"]);\n\nfunction hasInlineFormatChildren(childElements: XmlElement[]): boolean {\n return (\n childElements.length > 0 &&\n childElements.every((el) => INLINE_FORMAT_TAGS.has(getTagName(el)))\n );\n}\n\ntype TextRunResult = {\n text: string;\n bold?: boolean;\n italic?: boolean;\n underline?: boolean;\n strike?: boolean;\n highlight?: string;\n color?: string;\n href?: string;\n fontFamily?: string;\n};\n\nfunction extractTextRuns(\n children: XmlNode[],\n inheritBold?: boolean,\n inheritItalic?: boolean,\n inheritHref?: string,\n inheritUnderline?: boolean,\n inheritStrike?: boolean,\n inheritHighlight?: string,\n inheritColor?: string,\n inheritFontFamily?: string,\n): TextRunResult[] {\n const runs: TextRunResult[] = [];\n for (const child of children) {\n if (isTextNode(child)) {\n const run: TextRunResult = { text: child[\"#text\"] };\n if (inheritBold) run.bold = true;\n if (inheritItalic) run.italic = true;\n if (inheritUnderline) run.underline = true;\n if (inheritStrike) run.strike = true;\n if (inheritHighlight) run.highlight = inheritHighlight;\n if (inheritColor) run.color = inheritColor;\n if (inheritHref) run.href = inheritHref;\n if (inheritFontFamily) run.fontFamily = inheritFontFamily;\n runs.push(run);\n } else {\n const tag = getTagName(child);\n const innerChildren = getRawChildren(child);\n if (tag === \"B\") {\n runs.push(\n ...extractTextRuns(\n innerChildren,\n true,\n inheritItalic,\n inheritHref,\n inheritUnderline,\n inheritStrike,\n inheritHighlight,\n inheritColor,\n inheritFontFamily,\n ),\n );\n } else if (tag === \"I\") {\n runs.push(\n ...extractTextRuns(\n innerChildren,\n inheritBold,\n true,\n inheritHref,\n inheritUnderline,\n inheritStrike,\n inheritHighlight,\n inheritColor,\n inheritFontFamily,\n ),\n );\n } else if (tag === \"A\") {\n const href = getAttributes(child).href ?? \"\";\n runs.push(\n ...extractTextRuns(\n innerChildren,\n inheritBold,\n inheritItalic,\n href,\n inheritUnderline,\n inheritStrike,\n inheritHighlight,\n inheritColor,\n inheritFontFamily,\n ),\n );\n } else if (tag === \"U\") {\n runs.push(\n ...extractTextRuns(\n innerChildren,\n inheritBold,\n inheritItalic,\n inheritHref,\n true,\n inheritStrike,\n inheritHighlight,\n inheritColor,\n inheritFontFamily,\n ),\n );\n } else if (tag === \"S\") {\n runs.push(\n ...extractTextRuns(\n innerChildren,\n inheritBold,\n inheritItalic,\n inheritHref,\n inheritUnderline,\n true,\n inheritHighlight,\n inheritColor,\n inheritFontFamily,\n ),\n );\n } else if (tag === \"Mark\") {\n const rawColor = getAttributes(child).color;\n const color = rawColor && rawColor.trim() ? rawColor : \"FFFF00\";\n runs.push(\n ...extractTextRuns(\n innerChildren,\n inheritBold,\n inheritItalic,\n inheritHref,\n inheritUnderline,\n inheritStrike,\n color,\n inheritColor,\n inheritFontFamily,\n ),\n );\n } else if (tag === \"Span\") {\n const spanAttrs = getAttributes(child);\n const rawSpanColor = spanAttrs.color;\n const spanColor =\n rawSpanColor && rawSpanColor.trim() ? rawSpanColor : inheritColor;\n const rawSpanFontFamily = spanAttrs.fontFamily;\n const spanFontFamily =\n rawSpanFontFamily && rawSpanFontFamily.trim()\n ? rawSpanFontFamily\n : inheritFontFamily;\n runs.push(\n ...extractTextRuns(\n innerChildren,\n inheritBold,\n inheritItalic,\n inheritHref,\n inheritUnderline,\n inheritStrike,\n inheritHighlight,\n spanColor,\n spanFontFamily,\n ),\n );\n }\n }\n }\n return runs;\n}\n\nfunction buildRunsAndText(\n node: XmlElement,\n): { runs: TextRunResult[]; text: string } | null {\n const rawChildren = getRawChildren(node);\n const childElements = rawChildren.filter(\n (c): c is XmlElement => !isTextNode(c),\n );\n if (!hasInlineFormatChildren(childElements)) return null;\n const runs = extractTextRuns(rawChildren);\n const text = runs.map((r) => r.text).join(\"\");\n return { runs, text };\n}\n\nfunction coerceChildAttrs(\n parentTagName: string,\n tagName: string,\n attrs: Record<string, string>,\n errors: string[],\n): Record<string, unknown> {\n const rules = CHILD_ELEMENT_COERCION_MAP[tagName];\n const result: Record<string, unknown> = {};\n const { regular: regularAttrs, dotGroups } = expandDotNotation(attrs);\n\n // Process dot-notation attributes\n for (const [prefix, subAttrs] of Object.entries(dotGroups)) {\n if (rules && rules[prefix]) {\n result[prefix] = coerceDotGroup(\n prefix,\n subAttrs,\n rules[prefix],\n `${parentTagName}.${tagName}`,\n errors,\n );\n } else if (rules) {\n const knownAttrs = getKnownChildAttributes(tagName);\n const suggestion = findClosestMatch(prefix, knownAttrs);\n errors.push(\n `<${parentTagName}>.<${tagName}>: Unknown attribute \"${prefix}\"${suggestion ? `. Did you mean \"${suggestion}\"?` : \"\"}`,\n );\n } else {\n result[prefix] = {};\n for (const [subKey, subValue] of Object.entries(subAttrs)) {\n (result[prefix] as Record<string, unknown>)[subKey] =\n coerceFallback(subValue);\n }\n }\n }\n\n // Process regular attributes\n for (const [key, value] of Object.entries(regularAttrs)) {\n if (key in dotGroups) {\n if (rules && rules[key]) {\n const resolved = resolveMixedNotationShorthand(value, rules[key]);\n if (resolved.mode === \"ignore\") {\n continue;\n }\n if (resolved.mode === \"merge\") {\n result[key] = {\n ...resolved.value,\n ...(result[key] as Record<string, unknown>),\n };\n continue;\n }\n }\n errors.push(\n `<${parentTagName}>.<${tagName}>: Attribute \"${key}\" conflicts with dot-notation attributes. Use one or the other, not both`,\n );\n continue;\n }\n if (rules && rules[key]) {\n const coerced = coerceWithRule(value, rules[key]);\n if (coerced.error !== null) {\n errors.push(`<${parentTagName}>.<${tagName}>: ${coerced.error}`);\n } else {\n result[key] = coerced.value;\n }\n } else if (rules) {\n // Unknown attribute on child element\n const knownAttrs = getKnownChildAttributes(tagName);\n const suggestion = findClosestMatch(key, knownAttrs);\n errors.push(\n `<${parentTagName}>.<${tagName}>: Unknown attribute \"${key}\"${suggestion ? `. Did you mean \"${suggestion}\"?` : \"\"}`,\n );\n } else {\n result[key] = coerceFallback(value);\n }\n }\n return result;\n}\n\n// ===== Child element converters =====\ntype ChildElementConverter = (\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n node?: XmlElement,\n) => void;\n\nfunction convertProcessArrowChildren(\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const steps: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== \"ProcessArrowStep\") {\n errors.push(\n `Unknown child element <${tag}> inside <ProcessArrow>. Expected: <ProcessArrowStep>`,\n );\n continue;\n }\n steps.push(\n coerceChildAttrs(\"ProcessArrow\", tag, getAttributes(child), errors),\n );\n }\n result.steps = steps;\n}\n\nfunction convertPyramidChildren(\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const levels: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== \"PyramidLevel\") {\n errors.push(\n `Unknown child element <${tag}> inside <Pyramid>. Expected: <PyramidLevel>`,\n );\n continue;\n }\n levels.push(coerceChildAttrs(\"Pyramid\", tag, getAttributes(child), errors));\n }\n result.levels = levels;\n}\n\nfunction convertTimelineChildren(\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const items: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== \"TimelineItem\") {\n errors.push(\n `Unknown child element <${tag}> inside <Timeline>. Expected: <TimelineItem>`,\n );\n continue;\n }\n items.push(coerceChildAttrs(\"Timeline\", tag, getAttributes(child), errors));\n }\n result.items = items;\n}\n\nfunction convertMatrixChildren(\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const items: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"MatrixAxes\":\n result.axes = coerceChildAttrs(\n \"Matrix\",\n tag,\n getAttributes(child),\n errors,\n );\n break;\n case \"MatrixQuadrants\":\n result.quadrants = coerceChildAttrs(\n \"Matrix\",\n tag,\n getAttributes(child),\n errors,\n );\n break;\n case \"MatrixItem\":\n items.push(\n coerceChildAttrs(\"Matrix\", tag, getAttributes(child), errors),\n );\n break;\n default:\n errors.push(\n `Unknown child element <${tag}> inside <Matrix>. Expected: <MatrixAxes>, <MatrixQuadrants>, or <MatrixItem>`,\n );\n }\n }\n if (items.length > 0) {\n result.items = items;\n }\n}\n\nfunction convertFlowChildren(\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const nodes: Record<string, unknown>[] = [];\n const connections: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"FlowNode\":\n nodes.push(coerceChildAttrs(\"Flow\", tag, getAttributes(child), errors));\n break;\n case \"FlowConnection\":\n connections.push(\n coerceChildAttrs(\"Flow\", tag, getAttributes(child), errors),\n );\n break;\n default:\n errors.push(\n `Unknown child element <${tag}> inside <Flow>. Expected: <FlowNode> or <FlowConnection>`,\n );\n }\n }\n if (nodes.length > 0) {\n result.nodes = nodes;\n }\n if (connections.length > 0) {\n result.connections = connections;\n }\n}\n\nfunction convertChartChildren(\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const data: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== \"ChartSeries\") {\n errors.push(\n `Unknown child element <${tag}> inside <Chart>. Expected: <ChartSeries>`,\n );\n continue;\n }\n const attrs = getAttributes(child);\n const series: Record<string, unknown> = {\n labels: [],\n values: [],\n };\n if (attrs.name !== undefined) {\n // chartDataSchema.name は z.string().optional() なのでそのまま文字列として使用\n series.name = attrs.name;\n }\n\n for (const dp of getChildElements(child)) {\n const dpTag = getTagName(dp);\n if (dpTag !== \"ChartDataPoint\") {\n errors.push(\n `Unknown child element <${dpTag}> inside <ChartSeries>. Expected: <ChartDataPoint>`,\n );\n continue;\n }\n const dpAttrs = getAttributes(dp);\n if (dpAttrs.label === undefined) {\n errors.push('<ChartDataPoint> requires a \"label\" attribute');\n }\n if (dpAttrs.value === undefined) {\n errors.push('<ChartDataPoint> requires a \"value\" attribute');\n }\n if (dpAttrs.label === undefined || dpAttrs.value === undefined) {\n continue;\n }\n const numValue = Number(dpAttrs.value);\n if (isNaN(numValue)) {\n errors.push(\n `Cannot convert \"${dpAttrs.value}\" to number in <ChartDataPoint> \"value\" attribute`,\n );\n continue;\n }\n (series.labels as string[]).push(dpAttrs.label);\n (series.values as number[]).push(numValue);\n }\n data.push(series);\n }\n result.data = data;\n}\n\nfunction convertTableChildren(\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const columns: Record<string, unknown>[] = [];\n const rows: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"Col\":\n columns.push(\n coerceChildAttrs(\"Table\", tag, getAttributes(child), errors),\n );\n break;\n case \"Tr\": {\n const rowAttrs = getAttributes(child);\n const cells: Record<string, unknown>[] = [];\n for (const cellEl of getChildElements(child)) {\n const cellTag = getTagName(cellEl);\n if (cellTag !== \"Td\") {\n errors.push(\n `Unknown child element <${cellTag}> inside <Tr>. Expected: <Td>`,\n );\n continue;\n }\n const cellAttrs = coerceChildAttrs(\n \"Tr\",\n cellTag,\n getAttributes(cellEl),\n errors,\n );\n const runsResult = buildRunsAndText(cellEl);\n if (runsResult) {\n cellAttrs.runs = runsResult.runs;\n cellAttrs.text = runsResult.text;\n } else {\n const cellText = getTextContent(cellEl);\n if (cellText !== undefined && !(\"text\" in cellAttrs)) {\n cellAttrs.text = cellText;\n }\n }\n cells.push(cellAttrs);\n }\n const row: Record<string, unknown> = { cells };\n if (rowAttrs.height !== undefined) {\n const h = Number(rowAttrs.height);\n if (isNaN(h)) {\n errors.push(\n `Cannot convert \"${rowAttrs.height}\" to number in <Tr> \"height\" attribute`,\n );\n } else {\n row.height = h;\n }\n }\n rows.push(row);\n break;\n }\n default:\n errors.push(\n `Unknown child element <${tag}> inside <Table>. Expected: <Col> or <Tr>`,\n );\n }\n }\n if (columns.length > 0) {\n result.columns = columns;\n } else if (rows.length > 0) {\n // Col が未指定の場合、行のセル数(colspan 考慮)からデフォルトの columns を自動生成\n const maxCells = Math.max(\n ...rows.map((row) =>\n (row.cells as Record<string, unknown>[]).reduce(\n (sum, cell) => sum + ((cell.colspan as number) ?? 1),\n 0,\n ),\n ),\n );\n result.columns = Array.from({ length: maxCells }, () => ({}));\n }\n if (rows.length > 0) {\n result.rows = rows;\n }\n}\n\nfunction convertTreeItem(\n element: XmlElement,\n errors: string[],\n): Record<string, unknown> {\n const attrs = getAttributes(element);\n if (attrs.label === undefined) {\n errors.push('<TreeItem> requires a \"label\" attribute');\n }\n const item: Record<string, unknown> = {};\n if (attrs.label !== undefined) {\n item.label = attrs.label;\n }\n if (attrs.color !== undefined) {\n item.color = attrs.color;\n }\n const children = getChildElements(element);\n if (children.length > 0) {\n item.children = children\n .map((child) => {\n const tag = getTagName(child);\n if (tag !== \"TreeItem\") {\n errors.push(\n `Unknown child element <${tag}> inside <TreeItem>. Expected: <TreeItem>`,\n );\n return null;\n }\n return convertTreeItem(child, errors);\n })\n .filter((item): item is Record<string, unknown> => item !== null);\n }\n return item;\n}\n\nfunction convertTreeChildren(\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n if (childElements.length !== 1) {\n errors.push(\n `<Tree> must have exactly 1 <TreeItem> child element, but got ${childElements.length}`,\n );\n return;\n }\n const child = childElements[0];\n const tag = getTagName(child);\n if (tag !== \"TreeItem\") {\n errors.push(\n `Unknown child element <${tag}> inside <Tree>. Expected: <TreeItem>`,\n );\n return;\n }\n result.data = convertTreeItem(child, errors);\n}\n\nfunction convertListChildren(\n parentTag: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const items: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== \"Li\") {\n errors.push(\n `Unknown child element <${tag}> inside <${parentTag}>. Expected: <Li>`,\n );\n continue;\n }\n const attrs = coerceChildAttrs(\n parentTag,\n tag,\n getAttributes(child),\n errors,\n );\n const runsResult = buildRunsAndText(child);\n if (runsResult) {\n attrs.runs = runsResult.runs;\n attrs.text = runsResult.text;\n } else {\n const textContent = getTextContent(child);\n if (textContent !== undefined && !(\"text\" in attrs)) {\n attrs.text = textContent;\n }\n }\n items.push(attrs);\n }\n result.items = items;\n}\n\n// SVG 要素を XML 文字列に再構築する\nconst svgBuilder = new XMLBuilder({\n preserveOrder: true,\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n});\n\nfunction serializeSvgElement(svgElement: XmlElement): string {\n return String(svgBuilder.build([svgElement]));\n}\n\nfunction convertSvgChildren(\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n if (childElements.length !== 1) {\n errors.push(\n `<Svg>: Expected exactly one <svg> child element, but found ${childElements.length} child element(s)`,\n );\n return;\n }\n\n const child = childElements[0];\n const tag = getTagName(child);\n if (tag !== \"svg\") {\n errors.push(`<Svg>: Expected <svg> child element, but found <${tag}>`);\n return;\n }\n\n result.svgContent = serializeSvgElement(child);\n}\n\nfunction convertTextInlineChildren(\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n node?: XmlElement,\n): void {\n // インラインフォーマットタグ以外の子要素がある場合はエラー\n for (const el of childElements) {\n const tag = getTagName(el);\n if (!INLINE_FORMAT_TAGS.has(tag)) {\n errors.push(\n `<Text>: Unexpected child element <${tag}>. Only <B>, <I>, <A>, <U>, <S>, <Mark>, and <Span> are allowed inside <Text>`,\n );\n return;\n }\n }\n if (!node || childElements.length === 0) return;\n const runsResult = buildRunsAndText(node);\n if (runsResult) {\n result.runs = runsResult.runs;\n result.text = runsResult.text;\n }\n}\n\nconst CHILD_ELEMENT_CONVERTERS: Record<string, ChildElementConverter> = {\n text: convertTextInlineChildren,\n ul: (childElements, result, errors) =>\n convertListChildren(\"Ul\", childElements, result, errors),\n ol: (childElements, result, errors) =>\n convertListChildren(\"Ol\", childElements, result, errors),\n processArrow: convertProcessArrowChildren,\n pyramid: convertPyramidChildren,\n timeline: convertTimelineChildren,\n matrix: convertMatrixChildren,\n flow: convertFlowChildren,\n chart: convertChartChildren,\n table: convertTableChildren,\n tree: convertTreeChildren,\n svg: convertSvgChildren,\n};\n\n// ===== Node conversion =====\nfunction convertElement(\n node: XmlElement,\n errors: string[],\n): Record<string, unknown> | null {\n const tagName = getTagName(node);\n const nodeType = TAG_TO_TYPE[tagName];\n const attrs = getAttributes(node);\n const childElements = getChildElements(node);\n const textContent = getTextContent(node);\n\n if (nodeType) {\n return convertPomNode(\n nodeType,\n tagName,\n attrs,\n childElements,\n textContent,\n errors,\n node,\n );\n } else {\n errors.push(`Unknown tag: <${tagName}>`);\n return null;\n }\n}\n\nfunction convertPomNode(\n nodeType: string,\n tagName: string,\n attrs: Record<string, string>,\n childElements: XmlElement[],\n textContent: string | undefined,\n errors: string[],\n xmlNode?: XmlElement,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { type: nodeType };\n\n // Expand dot-notation attributes (e.g., fill.color=\"hex\" → { fill: { color: \"hex\" } })\n const { regular: regularAttrs, dotGroups } = expandDotNotation(attrs);\n\n for (const [prefix, subAttrs] of Object.entries(dotGroups)) {\n if (prefix === \"type\") continue;\n const rule = getCoercionRule(nodeType, prefix);\n if (rule) {\n result[prefix] = coerceDotGroup(prefix, subAttrs, rule, tagName, errors);\n } else {\n const knownAttrs = getKnownAttributes(nodeType);\n const suggestion = findClosestMatch(prefix, knownAttrs);\n if (suggestion) {\n errors.push(\n `<${tagName}>: Unknown attribute \"${prefix}\". Did you mean \"${suggestion}\"?`,\n );\n } else {\n errors.push(`<${tagName}>: Unknown attribute \"${prefix}\"`);\n }\n }\n }\n\n for (const [key, value] of Object.entries(regularAttrs)) {\n if (key === \"type\") continue;\n // Conflict check: dot-notation and regular attribute for the same key\n if (key in dotGroups) {\n const ruleForConflict = getCoercionRule(nodeType, key);\n if (ruleForConflict) {\n const resolved = resolveMixedNotationShorthand(value, ruleForConflict);\n if (resolved.mode === \"ignore\") {\n continue;\n }\n if (resolved.mode === \"merge\") {\n result[key] = {\n ...resolved.value,\n ...(result[key] as Record<string, unknown>),\n };\n continue;\n }\n }\n errors.push(\n `<${tagName}>: Attribute \"${key}\" conflicts with dot-notation attributes (e.g., \"${key}.xxx\"). Use one or the other, not both`,\n );\n continue;\n }\n const rule = getCoercionRule(nodeType, key);\n if (rule) {\n const coerced = coerceWithRule(value, rule);\n if (coerced.error !== null) {\n errors.push(`<${tagName}>: ${coerced.error}`);\n } else {\n result[key] = coerced.value;\n }\n } else if (UNIVERSAL_ATTRS.has(key)) {\n // Allow universal attributes (e.g., x/y for Layer children)\n result[key] = coerceFallback(value);\n } else {\n // Unknown attribute\n const knownAttrs = getKnownAttributes(nodeType);\n const suggestion = findClosestMatch(key, knownAttrs);\n if (suggestion) {\n errors.push(\n `<${tagName}>: Unknown attribute \"${key}\". Did you mean \"${suggestion}\"?`,\n );\n } else {\n errors.push(`<${tagName}>: Unknown attribute \"${key}\"`);\n }\n }\n }\n\n // Text content → text property for nodes that support it\n if (textContent !== undefined && TEXT_CONTENT_NODES.has(nodeType)) {\n if (!(\"text\" in result)) {\n result.text = textContent;\n }\n }\n\n // Child element notation for complex properties\n const childConverter = CHILD_ELEMENT_CONVERTERS[nodeType];\n if (childConverter && childElements.length > 0) {\n childConverter(childElements, result, errors, xmlNode);\n }\n // Children for container nodes\n else if (CONTAINER_TYPES.has(nodeType) && childElements.length > 0) {\n const convertedChildren = childElements\n .map((child) => convertElement(child, errors))\n .filter((child): child is Record<string, unknown> => child !== null);\n result.children = convertedChildren;\n }\n // Leaf nodes that shouldn't have child elements\n else if (\n !CONTAINER_TYPES.has(nodeType) &&\n !childConverter &&\n childElements.length > 0\n ) {\n errors.push(\n `<${tagName}>: Unexpected child elements. <${tagName}> does not accept child elements`,\n );\n }\n\n // Zod validation for leaf nodes\n if (!CONTAINER_TYPES.has(nodeType)) {\n validateLeafNode(nodeType, result, errors);\n }\n\n // Icon: normalize color / bgColor\n if (nodeType === \"icon\") {\n if (typeof result.color === \"string\" && !result.color.startsWith(\"#\")) {\n result.color = `#${result.color}`;\n }\n if (typeof result.bgColor === \"string\" && !result.bgColor.startsWith(\"#\")) {\n result.bgColor = `#${result.bgColor}`;\n }\n }\n\n // Svg: normalize color and validate svgContent\n if (nodeType === \"svg\") {\n if (typeof result.color === \"string\" && !result.color.startsWith(\"#\")) {\n result.color = `#${result.color}`;\n }\n if (result.svgContent === undefined) {\n errors.push(\"<Svg>: A <svg> child element is required\");\n }\n }\n\n return result;\n}\n\n/**\n * XML 文字列を POMNode 配列に変換する。\n *\n * 最上位は `<Slide>` 要素のみが許容される。各 `<Slide>` が 1 つのスライドに\n * 対応し、その子要素がスライドのルート POMNode となる。子要素が複数ある場合は\n * 暗黙的に VStack でラップされる。\n *\n * XML タグは POM ノードタイプにマッピングされ、属性値は Zod スキーマを参照して\n * 適切な型(number, boolean, array, object)に変換される。\n * 未知のタグ名が指定された場合はエラーがスローされる。\n *\n * @example\n * ```typescript\n * import { parseXml, buildPptx } from \"@hirokisakabe/pom\";\n *\n * const xml = `\n * <Slide>\n * <VStack gap=\"16\" padding=\"32\">\n * <Text fontSize=\"32\" bold=\"true\">売上レポート</Text>\n * </VStack>\n * </Slide>\n * `;\n *\n * const nodes = parseXml(xml);\n * const pptx = await buildPptx(nodes, { w: 1280, h: 720 });\n * ```\n */\nexport function parseXml(xmlString: string): POMNode[] {\n if (!xmlString.trim()) return [];\n\n const parser = new XMLParser({\n preserveOrder: true,\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n parseAttributeValue: false,\n parseTagValue: false,\n trimValues: false,\n });\n\n const wrappedXml = `<__root__>${xmlString}</__root__>`;\n const parsed: XmlElement[] = parser.parse(wrappedXml) as XmlElement[];\n\n if (!parsed || parsed.length === 0) return [];\n\n const rootElement = parsed[0];\n const rootChildren = (rootElement[\"__root__\"] ?? []) as XmlNode[];\n\n const errors: string[] = [];\n const slideElements = rootChildren.filter(\n (child): child is XmlElement => !isTextNode(child),\n );\n\n const nodes: POMNode[] = [];\n for (const slideEl of slideElements) {\n const tagName = getTagName(slideEl);\n if (tagName !== \"Slide\") {\n errors.push(\n `Top-level element must be <Slide>, but got <${tagName}>. Wrap your slide content in <Slide>...</Slide>.`,\n );\n continue;\n }\n if (Object.keys(getAttributes(slideEl)).length > 0) {\n errors.push(`<Slide>: Attributes are not supported`);\n }\n const slideChildren = getChildElements(slideEl);\n if (slideChildren.length === 0) {\n errors.push(`<Slide> must contain at least one child element`);\n continue;\n }\n const converted = slideChildren\n .map((child) => convertElement(child, errors))\n .filter((c): c is Record<string, unknown> => c !== null);\n if (converted.length === 0) continue;\n if (converted.length === 1) {\n nodes.push(converted[0] as POMNode);\n } else {\n nodes.push({\n type: \"vstack\",\n children: converted,\n } as POMNode);\n }\n }\n\n if (errors.length > 0) {\n throw new ParseXmlError(errors);\n }\n\n return nodes;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"parseXml.js","names":[],"sources":["../../src/parseXml/parseXml.ts"],"sourcesContent":["import { XMLBuilder, XMLParser } from \"fast-xml-parser\";\nimport type { z } from \"zod\";\nimport type { POMNode } from \"../types.ts\";\nimport {\n getNodeMetadata,\n getNodeMetadataByTag,\n NODE_METADATA,\n} from \"../registry/nodeMetadata.ts\";\nimport {\n INLINE_BOOLEAN_FORMATS,\n INLINE_FORMAT_TAG_LIST,\n INLINE_FORMAT_TAGS,\n INLINE_LINK_TAG,\n INLINE_MARK_TAG,\n INLINE_SPAN_TAG,\n MARK_DEFAULT_HIGHLIGHT_COLOR,\n formatExpectedTags,\n type NodeSpecificChildRule,\n type RepeatedChildRule,\n type TextRun,\n type XmlChildRule,\n} from \"../registry/xmlChildRules.ts\";\nimport {\n type CoercionRule,\n NODE_COERCION_MAP,\n CHILD_ELEMENT_COERCION_MAP,\n coerceWithRule,\n coerceFallback,\n getObjectShapeFromRule,\n resolveMixedNotationShorthand,\n} from \"./coercionRules.ts\";\n\n// ===== ParseXmlError =====\nexport class ParseXmlError extends Error {\n public readonly errors: string[];\n constructor(errors: string[]) {\n const message = `XML validation failed (${errors.length} error${errors.length > 1 ? \"s\" : \"\"}):\\n${errors.map((e) => ` - ${e}`).join(\"\\n\")}`;\n super(message);\n this.name = \"ParseXmlError\";\n this.errors = errors;\n }\n}\n\n// ===== Tag name → POM node type mapping =====\nexport const TAG_TO_TYPE: Record<string, string> = Object.fromEntries(\n NODE_METADATA.map((def) => [def.tagName, def.type]),\n);\n// Attributes allowed on any node (e.g., x/y for Layer children positioning)\nconst UNIVERSAL_ATTRS = new Set([\"x\", \"y\"]);\n\n// ===== Validation helpers =====\nfunction getKnownAttributes(nodeType: string): string[] {\n const rules = NODE_COERCION_MAP[nodeType];\n if (!rules) return [];\n return Object.keys(rules);\n}\n\nfunction levenshteinDistance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n const dp: number[][] = Array.from({ length: m + 1 }, () =>\n Array<number>(n + 1).fill(0),\n );\n for (let i = 0; i <= m; i++) dp[i][0] = i;\n for (let j = 0; j <= n; j++) dp[0][j] = j;\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i][j] =\n a[i - 1] === b[j - 1]\n ? dp[i - 1][j - 1]\n : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);\n }\n }\n return dp[m][n];\n}\n\nfunction findClosestMatch(\n input: string,\n candidates: string[],\n): string | undefined {\n const threshold = Math.max(2, Math.floor(input.length / 2));\n let bestMatch: string | undefined;\n let bestDistance = Infinity;\n for (const candidate of candidates) {\n const dist = levenshteinDistance(\n input.toLowerCase(),\n candidate.toLowerCase(),\n );\n if (dist < bestDistance && dist <= threshold) {\n bestDistance = dist;\n bestMatch = candidate;\n }\n }\n return bestMatch;\n}\n\nfunction getKnownChildAttributes(tagName: string): string[] {\n const rules = CHILD_ELEMENT_COERCION_MAP[tagName];\n if (!rules) return [];\n return Object.keys(rules);\n}\n\nfunction formatZodIssue(\n issue: z.core.$ZodIssue,\n tagName: string,\n): string | null {\n const path = issue.path;\n // Skip children-related issues (validated recursively)\n if (path.length > 0 && path[0] === \"children\") return null;\n // Skip \"type\" field issues (set internally)\n if (path.length === 1 && path[0] === \"type\") return null;\n\n const attrName = path.length > 0 ? String(path[0]) : undefined;\n\n const code = issue.code;\n\n if (code === \"invalid_type\") {\n // Missing required attribute\n if (issue.input === undefined) {\n if (attrName) {\n return `<${tagName}>: Missing required attribute \"${attrName}\"`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n // Type mismatch\n if (attrName) {\n return `<${tagName}>: Invalid type for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n if (code === \"invalid_value\") {\n if (attrName) {\n const values = (issue as unknown as { values: string[] }).values;\n if (values) {\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". Expected: ${values.map((v) => `\"${v}\"`).join(\", \")}`;\n }\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n if (code === \"too_small\" || code === \"too_big\") {\n if (attrName) {\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n // Generic fallback\n if (attrName) {\n return `<${tagName}>: Attribute \"${attrName}\": ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n}\n\n// Properties that may be legitimately absent when using child element notation\n// or when the property is optional in practice (even if required in schema).\nfunction validateLeafNode(\n nodeType: string,\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const def = getNodeMetadata(nodeType as POMNode[\"type\"]);\n if (def.childPolicy.kind === \"pom-children\") return;\n const schema = def.schema;\n const tagName = def.tagName;\n const optionalChildProps = new Set(\n def.childPolicy.kind === \"custom\"\n ? (def.childPolicy.optionalProperties ?? [])\n : [],\n );\n const parseResult = schema.safeParse(result);\n if (!parseResult.success) {\n const seen = new Set<string>();\n for (const issue of parseResult.error.issues) {\n // Skip only top-level missing child-element properties (path.length === 1)\n // Nested issues (e.g., data.children[0].label) must still be reported\n if (\n optionalChildProps.size > 0 &&\n issue.path.length === 1 &&\n optionalChildProps.has(String(issue.path[0])) &&\n issue.code === \"invalid_type\" &&\n issue.input === undefined\n ) {\n continue;\n }\n // Skip issues for universal attributes (x, y)\n if (issue.path.length > 0 && UNIVERSAL_ATTRS.has(String(issue.path[0]))) {\n continue;\n }\n const msg = formatZodIssue(issue, tagName);\n if (msg && !seen.has(msg)) {\n seen.add(msg);\n errors.push(msg);\n }\n }\n }\n}\n\n// ===== Types for XML parser output (preserveOrder mode) =====\ntype XmlNode = XmlElement | XmlTextNode;\ntype XmlTextNode = { \"#text\": string };\ninterface XmlElement {\n [tagName: string]: XmlNode[] | Record<string, string> | undefined;\n \":@\"?: Record<string, string>;\n}\n\n// ===== Coercion rule lookup =====\n\nfunction getCoercionRule(\n nodeType: string,\n propertyName: string,\n): CoercionRule | undefined {\n return NODE_COERCION_MAP[nodeType]?.[propertyName];\n}\n\n// ===== Dot notation helpers =====\n\n// ===== Dot notation expansion =====\nfunction expandDotNotation(attrs: Record<string, string>): {\n regular: Record<string, string>;\n dotGroups: Record<string, Record<string, string>>;\n} {\n const regular: Record<string, string> = {};\n const dotGroups: Record<string, Record<string, string>> = {};\n\n for (const [key, value] of Object.entries(attrs)) {\n const dotIndex = key.indexOf(\".\");\n if (dotIndex > 0) {\n const prefix = key.substring(0, dotIndex);\n const suffix = key.substring(dotIndex + 1);\n if (!dotGroups[prefix]) dotGroups[prefix] = {};\n dotGroups[prefix][suffix] = value;\n } else {\n regular[key] = value;\n }\n }\n\n return { regular, dotGroups };\n}\n\nfunction coerceDotGroup(\n prefix: string,\n subAttrs: Record<string, string>,\n rule: CoercionRule,\n tagName: string,\n errors: string[],\n): Record<string, unknown> {\n const objectShape = getObjectShapeFromRule(rule);\n\n const obj: Record<string, unknown> = {};\n if (objectShape) {\n for (const [subKey, subValue] of Object.entries(subAttrs)) {\n if (objectShape[subKey]) {\n const coerced = coerceWithRule(subValue, objectShape[subKey]);\n if (coerced.error !== null) {\n errors.push(`<${tagName}>: ${prefix}.${subKey}: ${coerced.error}`);\n } else {\n obj[subKey] = coerced.value;\n }\n } else {\n const knownSubKeys = Object.keys(objectShape);\n const suggestion = findClosestMatch(subKey, knownSubKeys);\n errors.push(\n `<${tagName}>: Unknown sub-attribute \"${prefix}.${subKey}\"${suggestion ? `. Did you mean \"${prefix}.${suggestion}\"?` : \"\"}`,\n );\n }\n }\n } else {\n errors.push(\n `<${tagName}>: Attribute \"${prefix}\" does not support dot notation`,\n );\n }\n return obj;\n}\n\n// ===== XML node helpers =====\nfunction isTextNode(node: XmlNode): node is XmlTextNode {\n return \"#text\" in node;\n}\n\nfunction getTagName(node: XmlElement): string {\n for (const key of Object.keys(node)) {\n if (key !== \":@\") return key;\n }\n throw new Error(\"No tag name found in XML element\");\n}\n\nfunction getAttributes(node: XmlElement): Record<string, string> {\n const attrs: Record<string, string> = {};\n const rawAttrs = node[\":@\"];\n if (rawAttrs) {\n for (const [key, value] of Object.entries(rawAttrs)) {\n const attrName = key.startsWith(\"@_\") ? key.slice(2) : key;\n attrs[attrName] = value.trim();\n }\n }\n return attrs;\n}\n\nfunction getChildElements(node: XmlElement): XmlElement[] {\n const tagName = getTagName(node);\n const children = node[tagName] as XmlNode[] | undefined;\n if (!children) return [];\n return children.filter((child): child is XmlElement => !isTextNode(child));\n}\n\nfunction getTextContent(node: XmlElement): string | undefined {\n const tagName = getTagName(node);\n const children = node[tagName] as XmlNode[] | undefined;\n if (!children) return undefined;\n const textParts: string[] = [];\n for (const child of children) {\n if (isTextNode(child)) {\n textParts.push(child[\"#text\"]);\n }\n }\n return textParts.length > 0 ? textParts.join(\"\") : undefined;\n}\n\nfunction getRawChildren(node: XmlElement): XmlNode[] {\n const tagName = getTagName(node);\n return (node[tagName] as XmlNode[] | undefined) ?? [];\n}\n\nfunction hasInlineFormatChildren(childElements: XmlElement[]): boolean {\n return (\n childElements.length > 0 &&\n childElements.every((el) => INLINE_FORMAT_TAGS.has(getTagName(el)))\n );\n}\n\nfunction extractTextRuns(\n children: XmlNode[],\n inherited: Partial<Omit<TextRun, \"text\">> = {},\n): TextRun[] {\n const runs: TextRun[] = [];\n for (const child of children) {\n if (isTextNode(child)) {\n runs.push({ text: child[\"#text\"], ...inherited });\n continue;\n }\n const tag = getTagName(child);\n const innerChildren = getRawChildren(child);\n const booleanFormat = INLINE_BOOLEAN_FORMATS.find(\n (format) => format.tag === tag,\n );\n if (booleanFormat) {\n runs.push(\n ...extractTextRuns(innerChildren, {\n ...inherited,\n [booleanFormat.property]: true,\n }),\n );\n } else if (tag === INLINE_LINK_TAG) {\n // href なしの <A> は外側の href を引き継がない(リンク解除として扱う)\n const next = { ...inherited };\n const href = getAttributes(child).href;\n if (href) {\n next.href = href;\n } else {\n delete next.href;\n }\n runs.push(...extractTextRuns(innerChildren, next));\n } else if (tag === INLINE_MARK_TAG) {\n const rawColor = getAttributes(child).color;\n const highlight =\n rawColor && rawColor.trim() ? rawColor : MARK_DEFAULT_HIGHLIGHT_COLOR;\n runs.push(...extractTextRuns(innerChildren, { ...inherited, highlight }));\n } else if (tag === INLINE_SPAN_TAG) {\n const spanAttrs = getAttributes(child);\n const next = { ...inherited };\n if (spanAttrs.color && spanAttrs.color.trim()) {\n next.color = spanAttrs.color;\n }\n if (spanAttrs.fontFamily && spanAttrs.fontFamily.trim()) {\n next.fontFamily = spanAttrs.fontFamily;\n }\n if (spanAttrs.letterSpacing && spanAttrs.letterSpacing.trim()) {\n next.letterSpacing = Number(spanAttrs.letterSpacing);\n }\n runs.push(...extractTextRuns(innerChildren, next));\n }\n }\n return runs;\n}\n\nfunction buildRunsAndText(\n node: XmlElement,\n): { runs: TextRun[]; text: string } | null {\n const rawChildren = getRawChildren(node);\n const childElements = rawChildren.filter(\n (c): c is XmlElement => !isTextNode(c),\n );\n if (!hasInlineFormatChildren(childElements)) return null;\n const runs = extractTextRuns(rawChildren);\n const text = runs.map((r) => r.text).join(\"\");\n return { runs, text };\n}\n\nfunction coerceChildAttrs(\n parentTagName: string,\n tagName: string,\n attrs: Record<string, string>,\n errors: string[],\n): Record<string, unknown> {\n const rules = CHILD_ELEMENT_COERCION_MAP[tagName];\n const result: Record<string, unknown> = {};\n const { regular: regularAttrs, dotGroups } = expandDotNotation(attrs);\n\n // Process dot-notation attributes\n for (const [prefix, subAttrs] of Object.entries(dotGroups)) {\n if (rules && rules[prefix]) {\n result[prefix] = coerceDotGroup(\n prefix,\n subAttrs,\n rules[prefix],\n `${parentTagName}.${tagName}`,\n errors,\n );\n } else if (rules) {\n const knownAttrs = getKnownChildAttributes(tagName);\n const suggestion = findClosestMatch(prefix, knownAttrs);\n errors.push(\n `<${parentTagName}>.<${tagName}>: Unknown attribute \"${prefix}\"${suggestion ? `. Did you mean \"${suggestion}\"?` : \"\"}`,\n );\n } else {\n result[prefix] = {};\n for (const [subKey, subValue] of Object.entries(subAttrs)) {\n (result[prefix] as Record<string, unknown>)[subKey] =\n coerceFallback(subValue);\n }\n }\n }\n\n // Process regular attributes\n for (const [key, value] of Object.entries(regularAttrs)) {\n if (key in dotGroups) {\n if (rules && rules[key]) {\n const resolved = resolveMixedNotationShorthand(value, rules[key]);\n if (resolved.mode === \"ignore\") {\n continue;\n }\n if (resolved.mode === \"merge\") {\n result[key] = {\n ...resolved.value,\n ...(result[key] as Record<string, unknown>),\n };\n continue;\n }\n }\n errors.push(\n `<${parentTagName}>.<${tagName}>: Attribute \"${key}\" conflicts with dot-notation attributes. Use one or the other, not both`,\n );\n continue;\n }\n if (rules && rules[key]) {\n const coerced = coerceWithRule(value, rules[key]);\n if (coerced.error !== null) {\n errors.push(`<${parentTagName}>.<${tagName}>: ${coerced.error}`);\n } else {\n result[key] = coerced.value;\n }\n } else if (rules) {\n // Unknown attribute on child element\n const knownAttrs = getKnownChildAttributes(tagName);\n const suggestion = findClosestMatch(key, knownAttrs);\n errors.push(\n `<${parentTagName}>.<${tagName}>: Unknown attribute \"${key}\"${suggestion ? `. Did you mean \"${suggestion}\"?` : \"\"}`,\n );\n } else {\n result[key] = coerceFallback(value);\n }\n }\n return result;\n}\n\n// ===== Child element converters =====\n\nfunction unknownChildError(\n childTag: string,\n parentTag: string,\n expectedTags: readonly string[],\n): string {\n return `Unknown child element <${childTag}> inside <${parentTag}>. Expected: ${formatExpectedTags(expectedTags)}`;\n}\n\n/** element の text content / インライン装飾を attrs の text / runs へ反映する */\nfunction applyTextAndRuns(\n element: XmlElement,\n attrs: Record<string, unknown>,\n): void {\n const runsResult = buildRunsAndText(element);\n if (runsResult) {\n attrs.runs = runsResult.runs;\n attrs.text = runsResult.text;\n } else {\n const textContent = getTextContent(element);\n if (textContent !== undefined && !(\"text\" in attrs)) {\n attrs.text = textContent;\n }\n }\n}\n\n/** RepeatedChildRule (単一種類の child tag の繰り返し → 配列 property) の汎用 converter */\nfunction convertRepeatedChildren(\n rule: RepeatedChildRule,\n parentTag: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const items: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== rule.childTag) {\n errors.push(unknownChildError(tag, parentTag, [rule.childTag]));\n continue;\n }\n const attrs = coerceChildAttrs(\n parentTag,\n tag,\n getAttributes(child),\n errors,\n );\n if (rule.allowsItemText) {\n applyTextAndRuns(child, attrs);\n }\n items.push(attrs);\n }\n result[rule.property] = items;\n}\n\n/** NodeSpecificChildRule を処理する専用 converter のシグネチャ */\ntype NodeSpecificChildConverter = (\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n) => void;\n\nfunction convertMatrixChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const items: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"MatrixAxes\":\n result.axes = coerceChildAttrs(\n tagName,\n tag,\n getAttributes(child),\n errors,\n );\n break;\n case \"MatrixQuadrants\":\n result.quadrants = coerceChildAttrs(\n tagName,\n tag,\n getAttributes(child),\n errors,\n );\n break;\n case \"MatrixItem\":\n items.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n default:\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n }\n }\n if (items.length > 0) {\n result.items = items;\n }\n}\n\nfunction convertFlowChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const nodes: Record<string, unknown>[] = [];\n const connections: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"FlowNode\":\n nodes.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n case \"FlowConnection\":\n connections.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n default:\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n }\n }\n if (nodes.length > 0) {\n result.nodes = nodes;\n }\n if (connections.length > 0) {\n result.connections = connections;\n }\n}\n\nfunction convertChartChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const data: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== \"ChartSeries\") {\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n continue;\n }\n const attrs = getAttributes(child);\n const series: Record<string, unknown> = {\n labels: [],\n values: [],\n };\n if (attrs.name !== undefined) {\n // chartDataSchema.name は z.string().optional() なのでそのまま文字列として使用\n series.name = attrs.name;\n }\n\n for (const dp of getChildElements(child)) {\n const dpTag = getTagName(dp);\n if (dpTag !== \"ChartDataPoint\") {\n errors.push(\n unknownChildError(dpTag, \"ChartSeries\", [\"ChartDataPoint\"]),\n );\n continue;\n }\n const dpAttrs = getAttributes(dp);\n if (dpAttrs.label === undefined) {\n errors.push('<ChartDataPoint> requires a \"label\" attribute');\n }\n if (dpAttrs.value === undefined) {\n errors.push('<ChartDataPoint> requires a \"value\" attribute');\n }\n if (dpAttrs.label === undefined || dpAttrs.value === undefined) {\n continue;\n }\n const numValue = Number(dpAttrs.value);\n if (isNaN(numValue)) {\n errors.push(\n `Cannot convert \"${dpAttrs.value}\" to number in <ChartDataPoint> \"value\" attribute`,\n );\n continue;\n }\n (series.labels as string[]).push(dpAttrs.label);\n (series.values as number[]).push(numValue);\n }\n data.push(series);\n }\n result.data = data;\n}\n\nfunction convertTableChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const columns: Record<string, unknown>[] = [];\n const rows: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"Col\":\n columns.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n case \"Tr\": {\n const rowAttrs = getAttributes(child);\n const cells: Record<string, unknown>[] = [];\n for (const cellEl of getChildElements(child)) {\n const cellTag = getTagName(cellEl);\n if (cellTag !== \"Td\") {\n errors.push(unknownChildError(cellTag, \"Tr\", [\"Td\"]));\n continue;\n }\n const cellAttrs = coerceChildAttrs(\n \"Tr\",\n cellTag,\n getAttributes(cellEl),\n errors,\n );\n applyTextAndRuns(cellEl, cellAttrs);\n cells.push(cellAttrs);\n }\n const row: Record<string, unknown> = { cells };\n if (rowAttrs.height !== undefined) {\n const h = Number(rowAttrs.height);\n if (isNaN(h)) {\n errors.push(\n `Cannot convert \"${rowAttrs.height}\" to number in <Tr> \"height\" attribute`,\n );\n } else {\n row.height = h;\n }\n }\n rows.push(row);\n break;\n }\n default:\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n }\n }\n if (columns.length > 0) {\n result.columns = columns;\n } else if (rows.length > 0) {\n // Col が未指定の場合、行のセル数(colspan 考慮)からデフォルトの columns を自動生成\n const maxCells = Math.max(\n ...rows.map((row) =>\n (row.cells as Record<string, unknown>[]).reduce(\n (sum, cell) => sum + ((cell.colspan as number) ?? 1),\n 0,\n ),\n ),\n );\n result.columns = Array.from({ length: maxCells }, () => ({}));\n }\n if (rows.length > 0) {\n result.rows = rows;\n }\n}\n\nfunction convertTreeItem(\n element: XmlElement,\n errors: string[],\n): Record<string, unknown> {\n const attrs = getAttributes(element);\n if (attrs.label === undefined) {\n errors.push('<TreeItem> requires a \"label\" attribute');\n }\n const item: Record<string, unknown> = {};\n if (attrs.label !== undefined) {\n item.label = attrs.label;\n }\n if (attrs.color !== undefined) {\n item.color = attrs.color;\n }\n if (attrs.textColor !== undefined) {\n item.textColor = attrs.textColor;\n }\n const children = getChildElements(element);\n if (children.length > 0) {\n item.children = children\n .map((child) => {\n const tag = getTagName(child);\n if (tag !== \"TreeItem\") {\n errors.push(unknownChildError(tag, \"TreeItem\", [\"TreeItem\"]));\n return null;\n }\n return convertTreeItem(child, errors);\n })\n .filter((item): item is Record<string, unknown> => item !== null);\n }\n return item;\n}\n\nfunction convertTreeChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n if (childElements.length !== 1) {\n errors.push(\n `<Tree> must have exactly 1 <TreeItem> child element, but got ${childElements.length}`,\n );\n return;\n }\n const child = childElements[0];\n const tag = getTagName(child);\n if (tag !== \"TreeItem\") {\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n return;\n }\n result.data = convertTreeItem(child, errors);\n}\n\n// SVG 要素を XML 文字列に再構築する\nconst svgBuilder = new XMLBuilder({\n preserveOrder: true,\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n});\n\nfunction serializeSvgElement(svgElement: XmlElement): string {\n return String(svgBuilder.build([svgElement]));\n}\n\nfunction convertSvgChildren(\n _rule: NodeSpecificChildRule,\n _tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n if (childElements.length !== 1) {\n errors.push(\n `<Svg>: Expected exactly one <svg> child element, but found ${childElements.length} child element(s)`,\n );\n return;\n }\n\n const child = childElements[0];\n const tag = getTagName(child);\n if (tag !== \"svg\") {\n errors.push(`<Svg>: Expected <svg> child element, but found <${tag}>`);\n return;\n }\n\n result.svgContent = serializeSvgElement(child);\n}\n\nfunction convertInlineRunsChildren(\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n node?: XmlElement,\n): void {\n // インラインフォーマットタグ以外の子要素がある場合はエラー\n for (const el of childElements) {\n const tag = getTagName(el);\n if (!INLINE_FORMAT_TAGS.has(tag)) {\n const allowedTags = INLINE_FORMAT_TAG_LIST.map((t) => `<${t}>`);\n const allowedList = `${allowedTags.slice(0, -1).join(\", \")}, and ${allowedTags[allowedTags.length - 1]}`;\n errors.push(\n `<${tagName}>: Unexpected child element <${tag}>. Only ${allowedList} are allowed inside <${tagName}>`,\n );\n return;\n }\n }\n if (!node || childElements.length === 0) return;\n const runsResult = buildRunsAndText(node);\n if (runsResult) {\n result.runs = runsResult.runs;\n result.text = runsResult.text;\n }\n}\n\n/** NodeSpecificChildRule を持つノードの専用 converter テーブル */\nconst NODE_SPECIFIC_CHILD_CONVERTERS: Record<\n string,\n NodeSpecificChildConverter\n> = {\n matrix: convertMatrixChildren,\n flow: convertFlowChildren,\n chart: convertChartChildren,\n table: convertTableChildren,\n tree: convertTreeChildren,\n svg: convertSvgChildren,\n};\n\n/** NodeMetadata の xmlChildRule に従って child element を変換する */\nfunction applyXmlChildRule(\n rule: XmlChildRule,\n nodeType: string,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n node?: XmlElement,\n): void {\n if (rule.kind === \"inline-runs\") {\n convertInlineRunsChildren(tagName, childElements, result, errors, node);\n return;\n }\n if (rule.kind === \"repeated\") {\n convertRepeatedChildren(rule, tagName, childElements, result, errors);\n return;\n }\n const converter = NODE_SPECIFIC_CHILD_CONVERTERS[nodeType];\n if (!converter) {\n throw new Error(\n `No node-specific child converter registered for node type: ${nodeType}`,\n );\n }\n converter(rule, tagName, childElements, result, errors);\n}\n\n// ===== Theme tokens =====\nconst THEME_TOKEN_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst THEME_TOKEN_VALUE_PATTERN = /^[0-9A-Fa-f]{6}$/;\n// 値全体が \"$name\"(\"#\" プレフィックスは任意)のときトークン参照とみなす\nconst TOKEN_REF_PATTERN = /^(#?)\\$([A-Za-z][A-Za-z0-9_-]*)$/;\n// backgroundGradient 文字列中に現れる \"$name\" 参照\nconst TOKEN_REF_IN_GRADIENT_PATTERN = /#?\\$([A-Za-z][A-Za-z0-9_-]*)/g;\n\nfunction parseThemeElement(\n element: XmlElement,\n tokens: Record<string, string>,\n errors: string[],\n): void {\n if (getChildElements(element).length > 0) {\n errors.push(\n `<Theme>: Child elements are not supported. Declare tokens as attributes (e.g. <Theme accent=\"1D4ED8\" />)`,\n );\n }\n for (const [name, rawValue] of Object.entries(getAttributes(element))) {\n if (!THEME_TOKEN_NAME_PATTERN.test(name)) {\n errors.push(\n `<Theme>: Invalid token name \"${name}\". Token names must start with a letter and contain only letters, digits, \"_\", and \"-\"`,\n );\n continue;\n }\n const value = rawValue.replace(/^#/, \"\");\n if (!THEME_TOKEN_VALUE_PATTERN.test(value)) {\n errors.push(\n `<Theme>: Invalid color value \"${rawValue}\" for token \"${name}\". Expected 6-digit hex (e.g. \"1D4ED8\")`,\n );\n continue;\n }\n tokens[name] = value;\n }\n}\n\n// トークン参照を解決する対象のキー判定。\n// pom の色属性は \"...Color\" / \"...Colors\" / \"highlight\" に統一されているため、\n// キー名ベースで判定することでテキスト内容(text 等)の \"$...\" を誤置換しない。\nfunction isColorKey(key: string): boolean {\n return /colors?$/i.test(key) || key === \"highlight\";\n}\n\nfunction resolveThemeToken(\n name: string,\n hashPrefix: string,\n tokens: Record<string, string>,\n themeDeclared: boolean,\n errors: string[],\n): string | undefined {\n const value = tokens[name];\n if (value !== undefined) {\n return `${hashPrefix}${value}`;\n }\n if (!themeDeclared) {\n errors.push(\n `Theme token \"$${name}\" is referenced, but no <Theme> is declared. Add a top-level <Theme ${name}=\"RRGGBB\" /> element`,\n );\n } else {\n const suggestion = findClosestMatch(name, Object.keys(tokens));\n errors.push(\n `Unknown theme token \"$${name}\"${suggestion ? `. Did you mean \"$${suggestion}\"?` : \"\"}`,\n );\n }\n return undefined;\n}\n\ninterface ThemeContext {\n tokens: Record<string, string>;\n declared: boolean;\n}\n\nfunction resolveThemeTokensDeep(\n value: unknown,\n key: string | null,\n tokens: Record<string, string>,\n themeDeclared: boolean,\n errors: string[],\n): unknown {\n if (typeof value === \"string\") {\n if (key === \"backgroundGradient\") {\n return value.replace(\n TOKEN_REF_IN_GRADIENT_PATTERN,\n (matched, name: string) => {\n const resolved = resolveThemeToken(\n name,\n \"#\",\n tokens,\n themeDeclared,\n errors,\n );\n return resolved ?? matched;\n },\n );\n }\n if (key !== null && isColorKey(key)) {\n const match = TOKEN_REF_PATTERN.exec(value.trim());\n if (match) {\n const resolved = resolveThemeToken(\n match[2],\n match[1],\n tokens,\n themeDeclared,\n errors,\n );\n return resolved ?? value;\n }\n }\n return value;\n }\n if (Array.isArray(value)) {\n // 配列はキー文脈を維持して要素ごとに解決する(chartColors 等の文字列配列に対応)\n return value.map((item) =>\n resolveThemeTokensDeep(item, key, tokens, themeDeclared, errors),\n );\n }\n if (typeof value === \"object\" && value !== null) {\n const result: Record<string, unknown> = {};\n for (const [childKey, childValue] of Object.entries(value)) {\n result[childKey] = resolveThemeTokensDeep(\n childValue,\n childKey,\n tokens,\n themeDeclared,\n errors,\n );\n }\n return result;\n }\n return value;\n}\n\n// ===== Node conversion =====\nfunction convertElement(\n node: XmlElement,\n errors: string[],\n theme: ThemeContext,\n): Record<string, unknown> | null {\n const tagName = getTagName(node);\n const def = getNodeMetadataByTag(tagName);\n const attrs = getAttributes(node);\n const childElements = getChildElements(node);\n const textContent = getTextContent(node);\n\n if (def) {\n return convertPomNode(\n def.type,\n tagName,\n attrs,\n childElements,\n textContent,\n errors,\n theme,\n node,\n );\n } else {\n errors.push(`Unknown tag: <${tagName}>`);\n return null;\n }\n}\n\nfunction convertPomNode(\n nodeType: string,\n tagName: string,\n attrs: Record<string, string>,\n childElements: XmlElement[],\n textContent: string | undefined,\n errors: string[],\n theme: ThemeContext,\n xmlNode?: XmlElement,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { type: nodeType };\n const def = getNodeMetadata(nodeType as POMNode[\"type\"]);\n\n // Expand dot-notation attributes (e.g., fill.color=\"hex\" → { fill: { color: \"hex\" } })\n const { regular: regularAttrs, dotGroups } = expandDotNotation(attrs);\n\n for (const [prefix, subAttrs] of Object.entries(dotGroups)) {\n if (prefix === \"type\") continue;\n const rule = getCoercionRule(nodeType, prefix);\n if (rule) {\n result[prefix] = coerceDotGroup(prefix, subAttrs, rule, tagName, errors);\n } else {\n const knownAttrs = getKnownAttributes(nodeType);\n const suggestion = findClosestMatch(prefix, knownAttrs);\n if (suggestion) {\n errors.push(\n `<${tagName}>: Unknown attribute \"${prefix}\". Did you mean \"${suggestion}\"?`,\n );\n } else {\n errors.push(`<${tagName}>: Unknown attribute \"${prefix}\"`);\n }\n }\n }\n\n for (const [key, value] of Object.entries(regularAttrs)) {\n if (key === \"type\") continue;\n // Conflict check: dot-notation and regular attribute for the same key\n if (key in dotGroups) {\n const ruleForConflict = getCoercionRule(nodeType, key);\n if (ruleForConflict) {\n const resolved = resolveMixedNotationShorthand(value, ruleForConflict);\n if (resolved.mode === \"ignore\") {\n continue;\n }\n if (resolved.mode === \"merge\") {\n result[key] = {\n ...resolved.value,\n ...(result[key] as Record<string, unknown>),\n };\n continue;\n }\n }\n errors.push(\n `<${tagName}>: Attribute \"${key}\" conflicts with dot-notation attributes (e.g., \"${key}.xxx\"). Use one or the other, not both`,\n );\n continue;\n }\n const rule = getCoercionRule(nodeType, key);\n if (rule) {\n const coerced = coerceWithRule(value, rule);\n if (coerced.error !== null) {\n errors.push(`<${tagName}>: ${coerced.error}`);\n } else {\n result[key] = coerced.value;\n }\n } else if (UNIVERSAL_ATTRS.has(key)) {\n // Allow universal attributes (e.g., x/y for Layer children)\n result[key] = coerceFallback(value);\n } else {\n // Unknown attribute\n const knownAttrs = getKnownAttributes(nodeType);\n const suggestion = findClosestMatch(key, knownAttrs);\n if (suggestion) {\n errors.push(\n `<${tagName}>: Unknown attribute \"${key}\". Did you mean \"${suggestion}\"?`,\n );\n } else {\n errors.push(`<${tagName}>: Unknown attribute \"${key}\"`);\n }\n }\n }\n\n // Text content → text property for nodes that support it\n if (textContent !== undefined && def.textContentProperty) {\n if (!(def.textContentProperty in result)) {\n result[def.textContentProperty] = textContent;\n }\n }\n\n // Child element notation for complex properties\n const childRule = def.xmlChildRule;\n if (childRule && childElements.length > 0) {\n applyXmlChildRule(\n childRule,\n nodeType,\n tagName,\n childElements,\n result,\n errors,\n xmlNode,\n );\n }\n // Children for container nodes\n else if (\n def.childPolicy.kind === \"pom-children\" &&\n childElements.length > 0\n ) {\n const convertedChildren = childElements\n .map((child) => convertElement(child, errors, theme))\n .filter((child): child is Record<string, unknown> => child !== null);\n result.children = convertedChildren;\n }\n // Leaf nodes that shouldn't have child elements\n else if (\n def.childPolicy.kind !== \"pom-children\" &&\n !childRule &&\n childElements.length > 0\n ) {\n errors.push(\n `<${tagName}>: Unexpected child elements. <${tagName}> does not accept child elements`,\n );\n }\n\n // テーマトークン参照(色属性中の \"$name\")を Zod 検証より前に解決する。\n // children は各子ノードの convertPomNode で解決済みのためスキップする。\n for (const [key, value] of Object.entries(result)) {\n if (key === \"type\" || key === \"children\") continue;\n result[key] = resolveThemeTokensDeep(\n value,\n key,\n theme.tokens,\n theme.declared,\n errors,\n );\n }\n\n // Zod validation for leaf nodes\n if (def.childPolicy.kind !== \"pom-children\") {\n validateLeafNode(nodeType, result, errors);\n }\n\n // Icon: normalize color / bgColor\n if (nodeType === \"icon\") {\n if (typeof result.color === \"string\" && !result.color.startsWith(\"#\")) {\n result.color = `#${result.color}`;\n }\n if (typeof result.bgColor === \"string\" && !result.bgColor.startsWith(\"#\")) {\n result.bgColor = `#${result.bgColor}`;\n }\n }\n\n // Svg: normalize color and validate svgContent\n if (nodeType === \"svg\") {\n if (typeof result.color === \"string\" && !result.color.startsWith(\"#\")) {\n result.color = `#${result.color}`;\n }\n if (result.svgContent === undefined) {\n errors.push(\"<Svg>: A <svg> child element is required\");\n }\n }\n\n return result;\n}\n\n/**\n * XML 文字列を POMNode 配列に変換する。\n *\n * 最上位は `<Slide>` 要素と `<Theme>` 要素のみが許容される。各 `<Slide>` が\n * 1 つのスライドに対応し、その子要素がスライドのルート POMNode となる。\n * 子要素が複数ある場合は暗黙的に VStack でラップされる。\n *\n * `<Theme>` は文書全体に適用されるデザイントークン(配色)の宣言で、最大 1 つ\n * 置ける。属性名がトークン名、属性値が 6 桁 hex の色値となり、各ノードの色属性\n * から `$トークン名` で参照できる。参照は parse 時に解決されるため、返される\n * POMNode には解決済みの hex 値が入る(`<Theme>` 自体はノードにならない)。\n *\n * XML タグは POM ノードタイプにマッピングされ、属性値は Zod スキーマを参照して\n * 適切な型(number, boolean, array, object)に変換される。\n * 未知のタグ名が指定された場合はエラーがスローされる。\n *\n * @example\n * ```typescript\n * import { parseXml, buildPptx } from \"@hirokisakabe/pom\";\n *\n * const xml = `\n * <Slide>\n * <VStack gap=\"16\" padding=\"32\">\n * <Text fontSize=\"32\" bold=\"true\">売上レポート</Text>\n * </VStack>\n * </Slide>\n * `;\n *\n * const nodes = parseXml(xml);\n * const pptx = await buildPptx(nodes, { w: 1280, h: 720 });\n * ```\n */\nexport function parseXml(xmlString: string): POMNode[] {\n if (!xmlString.trim()) return [];\n\n const parser = new XMLParser({\n preserveOrder: true,\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n parseAttributeValue: false,\n parseTagValue: false,\n trimValues: false,\n });\n\n const wrappedXml = `<__root__>${xmlString}</__root__>`;\n const parsed: XmlElement[] = parser.parse(wrappedXml) as XmlElement[];\n\n if (!parsed || parsed.length === 0) return [];\n\n const rootElement = parsed[0];\n const rootChildren = (rootElement[\"__root__\"] ?? []) as XmlNode[];\n\n const errors: string[] = [];\n const topLevelElements = rootChildren.filter(\n (child): child is XmlElement => !isTextNode(child),\n );\n\n // <Theme> はスライドより先に収集する(出現位置によらず文書全体に適用)\n const theme: ThemeContext = { tokens: {}, declared: false };\n for (const element of topLevelElements) {\n if (getTagName(element) !== \"Theme\") continue;\n if (theme.declared) {\n errors.push(\n `Only one <Theme> element is allowed, but multiple were found`,\n );\n continue;\n }\n theme.declared = true;\n parseThemeElement(element, theme.tokens, errors);\n }\n\n const slideElements = topLevelElements.filter(\n (element) => getTagName(element) !== \"Theme\",\n );\n\n const nodes: POMNode[] = [];\n for (const slideEl of slideElements) {\n const tagName = getTagName(slideEl);\n if (tagName !== \"Slide\") {\n errors.push(\n `Top-level element must be <Slide> or <Theme>, but got <${tagName}>. Wrap your slide content in <Slide>...</Slide>.`,\n );\n continue;\n }\n if (Object.keys(getAttributes(slideEl)).length > 0) {\n errors.push(`<Slide>: Attributes are not supported`);\n }\n const slideChildren = getChildElements(slideEl);\n if (slideChildren.length === 0) {\n errors.push(`<Slide> must contain at least one child element`);\n continue;\n }\n const converted = slideChildren\n .map((child) => convertElement(child, errors, theme))\n .filter((c): c is Record<string, unknown> => c !== null);\n if (converted.length === 0) continue;\n if (converted.length === 1) {\n nodes.push(converted[0] as POMNode);\n } else {\n nodes.push({\n type: \"vstack\",\n children: converted,\n } as POMNode);\n }\n }\n\n if (errors.length > 0) {\n throw new ParseXmlError(errors);\n }\n\n return nodes;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"serializeXml.d.ts","names":[],"sources":["../../src/parseXml/serializeXml.ts"],"mappings":";;;;;AA8JA;;;;AAA6C;;;;;;;;;iBAA7B,YAAA,CAAa,KAAgB,EAAT,OAAO"}
1
+ {"version":3,"file":"serializeXml.d.ts","names":[],"sources":["../../src/parseXml/serializeXml.ts"],"mappings":";;;;;AAoJA;;;;AAA6C;;;;;;;;;iBAA7B,YAAA,CAAa,KAAgB,EAAT,OAAO"}
@@ -1,18 +1,12 @@
1
- import { TAG_TO_TYPE } from "./parseXml.js";
1
+ import { getNodeMetadata } from "../registry/nodeMetadata.js";
2
+ import { INLINE_BOOLEAN_FORMATS, INLINE_MARK_TAG, INLINE_SPAN_TAG } from "../registry/xmlChildRules.js";
2
3
  //#region src/parseXml/serializeXml.ts
3
- const TYPE_TO_TAG = Object.fromEntries(Object.entries(TAG_TO_TYPE).map(([tag, type]) => [type, tag]));
4
4
  const SKIP_KEYS = new Set([
5
5
  "type",
6
6
  "children",
7
7
  "runs",
8
8
  "svgContent"
9
9
  ]);
10
- const INLINE_CONTENT_TYPES = new Set(["text", "shape"]);
11
- const CONTAINER_TYPES = new Set([
12
- "vstack",
13
- "hstack",
14
- "layer"
15
- ]);
16
10
  function escapeAttrValue(value) {
17
11
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
18
12
  }
@@ -38,15 +32,16 @@ function serializeAttrs(node) {
38
32
  function serializeRun(run) {
39
33
  let content = escapeXmlContent(run.text);
40
34
  if (run.href) content = `<A href="${escapeAttrValue(run.href)}">${content}</A>`;
41
- if (run.highlight) content = `<Mark color="${escapeAttrValue(run.highlight)}">${content}</Mark>`;
35
+ if (run.highlight) content = `<${INLINE_MARK_TAG} color="${escapeAttrValue(run.highlight)}">${content}</${INLINE_MARK_TAG}>`;
42
36
  const spanAttrs = [];
43
37
  if (run.color) spanAttrs.push(`color="${escapeAttrValue(run.color)}"`);
44
38
  if (run.fontFamily) spanAttrs.push(`fontFamily="${escapeAttrValue(run.fontFamily)}"`);
45
- if (spanAttrs.length > 0) content = `<Span ${spanAttrs.join(" ")}>${content}</Span>`;
46
- if (run.strike) content = `<S>${content}</S>`;
47
- if (run.underline) content = `<U>${content}</U>`;
48
- if (run.italic) content = `<I>${content}</I>`;
49
- if (run.bold) content = `<B>${content}</B>`;
39
+ if (run.letterSpacing !== void 0) spanAttrs.push(`letterSpacing="${run.letterSpacing}"`);
40
+ if (spanAttrs.length > 0) content = `<${INLINE_SPAN_TAG} ${spanAttrs.join(" ")}>${content}</${INLINE_SPAN_TAG}>`;
41
+ for (let i = INLINE_BOOLEAN_FORMATS.length - 1; i >= 0; i--) {
42
+ const { tag, property } = INLINE_BOOLEAN_FORMATS[i];
43
+ if (run[property]) content = `<${tag}>${content}</${tag}>`;
44
+ }
50
45
  return content;
51
46
  }
52
47
  function serializeRuns(runs) {
@@ -54,9 +49,10 @@ function serializeRuns(runs) {
54
49
  }
55
50
  function serializeNode(node, depth) {
56
51
  const indent = " ".repeat(depth);
57
- const tag = TYPE_TO_TAG[node.type];
52
+ const def = getNodeMetadata(node.type);
53
+ const tag = def.tagName;
58
54
  const nodeRecord = node;
59
- if (CONTAINER_TYPES.has(node.type)) {
55
+ if (def.childPolicy.kind === "pom-children") {
60
56
  const children = nodeRecord.children ?? [];
61
57
  const attrStr = serializeAttrs(nodeRecord);
62
58
  if (children.length === 0) return `${indent}<${tag}${attrStr} />`;
@@ -66,7 +62,7 @@ function serializeNode(node, depth) {
66
62
  const svgContent = nodeRecord.svgContent ?? "";
67
63
  return `${indent}<${tag}${serializeAttrs(nodeRecord)}>\n${svgContent}\n${indent}</${tag}>`;
68
64
  }
69
- if (INLINE_CONTENT_TYPES.has(node.type) && Array.isArray(nodeRecord.runs) && nodeRecord.runs.length > 0) {
65
+ if (def.supportsInlineRuns && Array.isArray(nodeRecord.runs) && nodeRecord.runs.length > 0) {
70
66
  const runs = nodeRecord.runs;
71
67
  return `${indent}<${tag}${serializeAttrs({
72
68
  ...nodeRecord,
@@ -1 +1 @@
1
- {"version":3,"file":"serializeXml.js","names":[],"sources":["../../src/parseXml/serializeXml.ts"],"sourcesContent":["import type { POMNode } from \"../types.ts\";\nimport { TAG_TO_TYPE } from \"./parseXml.ts\";\n\nconst TYPE_TO_TAG: Record<string, string> = Object.fromEntries(\n Object.entries(TAG_TO_TYPE).map(([tag, type]) => [type, tag]),\n);\n\n// runs と svgContent は専用の直列化パスで処理する\nconst SKIP_KEYS = new Set([\"type\", \"children\", \"runs\", \"svgContent\"]);\n\n// runs によるインライン装飾を child element として直列化するノードタイプ\nconst INLINE_CONTENT_TYPES = new Set([\"text\", \"shape\"]);\n\nconst CONTAINER_TYPES = new Set([\"vstack\", \"hstack\", \"layer\"]);\n\ninterface TextRun {\n text: string;\n bold?: boolean;\n italic?: boolean;\n underline?: boolean;\n strike?: boolean;\n highlight?: string;\n color?: string;\n href?: string;\n fontFamily?: string;\n}\n\nfunction escapeAttrValue(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\nfunction escapeXmlContent(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\nfunction serializePrimitive(value: string | number | boolean): string {\n if (typeof value === \"string\") return escapeAttrValue(value);\n if (typeof value === \"number\") return String(value);\n return value ? \"true\" : \"false\";\n}\n\nfunction serializeAttrs(node: Record<string, unknown>): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(node)) {\n if (SKIP_KEYS.has(key) || value === undefined) continue;\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n for (const [subKey, subValue] of Object.entries(\n value as Record<string, unknown>,\n )) {\n if (subValue !== undefined) {\n parts.push(\n `${key}.${subKey}=\"${serializePrimitive(subValue as string | number | boolean)}\"`,\n );\n }\n }\n } else if (Array.isArray(value)) {\n parts.push(`${key}=\"${escapeAttrValue(JSON.stringify(value))}\"`);\n } else if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n parts.push(`${key}=\"${serializePrimitive(value)}\"`);\n }\n }\n return parts.length > 0 ? \" \" + parts.join(\" \") : \"\";\n}\n\nfunction serializeRun(run: TextRun): string {\n let content = escapeXmlContent(run.text);\n\n if (run.href) {\n content = `<A href=\"${escapeAttrValue(run.href)}\">${content}</A>`;\n }\n if (run.highlight) {\n content = `<Mark color=\"${escapeAttrValue(run.highlight)}\">${content}</Mark>`;\n }\n const spanAttrs: string[] = [];\n if (run.color) spanAttrs.push(`color=\"${escapeAttrValue(run.color)}\"`);\n if (run.fontFamily)\n spanAttrs.push(`fontFamily=\"${escapeAttrValue(run.fontFamily)}\"`);\n if (spanAttrs.length > 0) {\n content = `<Span ${spanAttrs.join(\" \")}>${content}</Span>`;\n }\n if (run.strike) content = `<S>${content}</S>`;\n if (run.underline) content = `<U>${content}</U>`;\n if (run.italic) content = `<I>${content}</I>`;\n if (run.bold) content = `<B>${content}</B>`;\n return content;\n}\n\nfunction serializeRuns(runs: TextRun[]): string {\n return runs.map(serializeRun).join(\"\");\n}\n\nfunction serializeNode(node: POMNode, depth: number): string {\n const indent = \" \".repeat(depth);\n const tag = TYPE_TO_TAG[node.type];\n const nodeRecord = node as Record<string, unknown>;\n\n if (CONTAINER_TYPES.has(node.type)) {\n const children = (nodeRecord.children as POMNode[]) ?? [];\n const attrStr = serializeAttrs(nodeRecord);\n if (children.length === 0) {\n return `${indent}<${tag}${attrStr} />`;\n }\n const childrenStr = children\n .map((c) => serializeNode(c, depth + 1))\n .join(\"\\n\");\n return `${indent}<${tag}${attrStr}>\\n${childrenStr}\\n${indent}</${tag}>`;\n }\n\n if (node.type === \"svg\") {\n const svgContent = (nodeRecord.svgContent as string) ?? \"\";\n const attrStr = serializeAttrs(nodeRecord);\n return `${indent}<${tag}${attrStr}>\\n${svgContent}\\n${indent}</${tag}>`;\n }\n\n // Text / Shape: runs があればインライン child element として直列化し装飾を保持する\n if (\n INLINE_CONTENT_TYPES.has(node.type) &&\n Array.isArray(nodeRecord.runs) &&\n (nodeRecord.runs as unknown[]).length > 0\n ) {\n const runs = nodeRecord.runs as TextRun[];\n // runs がある場合、text は runs から復元できるため属性からも除外する\n const attrsWithoutText = { ...nodeRecord, text: undefined };\n const attrStr = serializeAttrs(attrsWithoutText);\n const inlineContent = serializeRuns(runs);\n return `${indent}<${tag}${attrStr}>${inlineContent}</${tag}>`;\n }\n\n const attrStr = serializeAttrs(nodeRecord);\n return `${indent}<${tag}${attrStr} />`;\n}\n\n/**\n * POMNode 配列を XML 文字列に変換する。\n *\n * parseXml の逆操作として機能する。runs(インライン装飾)は B/I/A/U/S/Mark/Span\n * タグとして child element に直列化されるため、テキストの装飾情報も保持される。\n *\n * @example\n * ```typescript\n * import { parseXml, serializeXml } from \"@hirokisakabe/pom\";\n *\n * const nodes = parseXml(xml);\n * // ... ノードの並び替えなど ...\n * const newXml = serializeXml(nodes);\n * ```\n */\nexport function serializeXml(nodes: POMNode[]): string {\n return nodes\n .map((node) => `<Slide>\\n${serializeNode(node, 1)}\\n</Slide>`)\n .join(\"\\n\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"serializeXml.js","names":[],"sources":["../../src/parseXml/serializeXml.ts"],"sourcesContent":["import type { POMNode } from \"../types.ts\";\nimport { getNodeMetadata } from \"../registry/nodeMetadata.ts\";\nimport {\n INLINE_BOOLEAN_FORMATS,\n INLINE_LINK_TAG,\n INLINE_MARK_TAG,\n INLINE_SPAN_TAG,\n type TextRun,\n} from \"../registry/xmlChildRules.ts\";\n\n// runs と svgContent は専用の直列化パスで処理する\nconst SKIP_KEYS = new Set([\"type\", \"children\", \"runs\", \"svgContent\"]);\n\nfunction escapeAttrValue(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\nfunction escapeXmlContent(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\nfunction serializePrimitive(value: string | number | boolean): string {\n if (typeof value === \"string\") return escapeAttrValue(value);\n if (typeof value === \"number\") return String(value);\n return value ? \"true\" : \"false\";\n}\n\nfunction serializeAttrs(node: Record<string, unknown>): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(node)) {\n if (SKIP_KEYS.has(key) || value === undefined) continue;\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n for (const [subKey, subValue] of Object.entries(\n value as Record<string, unknown>,\n )) {\n if (subValue !== undefined) {\n parts.push(\n `${key}.${subKey}=\"${serializePrimitive(subValue as string | number | boolean)}\"`,\n );\n }\n }\n } else if (Array.isArray(value)) {\n parts.push(`${key}=\"${escapeAttrValue(JSON.stringify(value))}\"`);\n } else if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n parts.push(`${key}=\"${serializePrimitive(value)}\"`);\n }\n }\n return parts.length > 0 ? \" \" + parts.join(\" \") : \"\";\n}\n\nfunction serializeRun(run: TextRun): string {\n let content = escapeXmlContent(run.text);\n\n if (run.href) {\n content = `<${INLINE_LINK_TAG} href=\"${escapeAttrValue(run.href)}\">${content}</${INLINE_LINK_TAG}>`;\n }\n if (run.highlight) {\n content = `<${INLINE_MARK_TAG} color=\"${escapeAttrValue(run.highlight)}\">${content}</${INLINE_MARK_TAG}>`;\n }\n const spanAttrs: string[] = [];\n if (run.color) spanAttrs.push(`color=\"${escapeAttrValue(run.color)}\"`);\n if (run.fontFamily)\n spanAttrs.push(`fontFamily=\"${escapeAttrValue(run.fontFamily)}\"`);\n if (run.letterSpacing !== undefined)\n spanAttrs.push(`letterSpacing=\"${run.letterSpacing}\"`);\n if (spanAttrs.length > 0) {\n content = `<${INLINE_SPAN_TAG} ${spanAttrs.join(\" \")}>${content}</${INLINE_SPAN_TAG}>`;\n }\n // INLINE_BOOLEAN_FORMATS は外側→内側のネスト順なので、内側から順に包む\n for (let i = INLINE_BOOLEAN_FORMATS.length - 1; i >= 0; i--) {\n const { tag, property } = INLINE_BOOLEAN_FORMATS[i];\n if (run[property]) content = `<${tag}>${content}</${tag}>`;\n }\n return content;\n}\n\nfunction serializeRuns(runs: TextRun[]): string {\n return runs.map(serializeRun).join(\"\");\n}\n\nfunction serializeNode(node: POMNode, depth: number): string {\n const indent = \" \".repeat(depth);\n const def = getNodeMetadata(node.type);\n const tag = def.tagName;\n const nodeRecord = node as Record<string, unknown>;\n\n if (def.childPolicy.kind === \"pom-children\") {\n const children = (nodeRecord.children as POMNode[]) ?? [];\n const attrStr = serializeAttrs(nodeRecord);\n if (children.length === 0) {\n return `${indent}<${tag}${attrStr} />`;\n }\n const childrenStr = children\n .map((c) => serializeNode(c, depth + 1))\n .join(\"\\n\");\n return `${indent}<${tag}${attrStr}>\\n${childrenStr}\\n${indent}</${tag}>`;\n }\n\n if (node.type === \"svg\") {\n const svgContent = (nodeRecord.svgContent as string) ?? \"\";\n const attrStr = serializeAttrs(nodeRecord);\n return `${indent}<${tag}${attrStr}>\\n${svgContent}\\n${indent}</${tag}>`;\n }\n\n // Text / Shape: runs があればインライン child element として直列化し装飾を保持する\n if (\n def.supportsInlineRuns &&\n Array.isArray(nodeRecord.runs) &&\n (nodeRecord.runs as unknown[]).length > 0\n ) {\n const runs = nodeRecord.runs as TextRun[];\n // runs がある場合、text は runs から復元できるため属性からも除外する\n const attrsWithoutText = { ...nodeRecord, text: undefined };\n const attrStr = serializeAttrs(attrsWithoutText);\n const inlineContent = serializeRuns(runs);\n return `${indent}<${tag}${attrStr}>${inlineContent}</${tag}>`;\n }\n\n const attrStr = serializeAttrs(nodeRecord);\n return `${indent}<${tag}${attrStr} />`;\n}\n\n/**\n * POMNode 配列を XML 文字列に変換する。\n *\n * parseXml の逆操作として機能する。runs(インライン装飾)は B/I/A/U/S/Mark/Span\n * タグとして child element に直列化されるため、テキストの装飾情報も保持される。\n *\n * @example\n * ```typescript\n * import { parseXml, serializeXml } from \"@hirokisakabe/pom\";\n *\n * const nodes = parseXml(xml);\n * // ... ノードの並び替えなど ...\n * const newXml = serializeXml(nodes);\n * ```\n */\nexport function serializeXml(nodes: POMNode[]): string {\n return nodes\n .map((node) => `<Slide>\\n${serializeNode(node, 1)}\\n</Slide>`)\n .join(\"\\n\");\n}\n"],"mappings":";;;AAWA,MAAM,YAAY,IAAI,IAAI;CAAC;CAAQ;CAAY;CAAQ;AAAY,CAAC;AAEpE,SAAS,gBAAgB,OAAuB;CAC9C,OAAO,MACJ,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,QAAQ,CAAC,CACvB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAAM;AACzB;AAEA,SAAS,iBAAiB,OAAuB;CAC/C,OAAO,MACJ,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAAM;AACzB;AAEA,SAAS,mBAAmB,OAA0C;CACpE,IAAI,OAAO,UAAU,UAAU,OAAO,gBAAgB,KAAK;CAC3D,IAAI,OAAO,UAAU,UAAU,OAAO,OAAO,KAAK;CAClD,OAAO,QAAQ,SAAS;AAC1B;AAEA,SAAS,eAAe,MAAuC;CAC7D,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,GAAG;EAC/C,IAAI,UAAU,IAAI,GAAG,KAAK,UAAU,KAAA,GAAW;EAC/C,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;QAChE,MAAM,CAAC,QAAQ,aAAa,OAAO,QACtC,KACF,GACE,IAAI,aAAa,KAAA,GACf,MAAM,KACJ,GAAG,IAAI,GAAG,OAAO,IAAI,mBAAmB,QAAqC,EAAE,EACjF;EAAA,OAGC,IAAI,MAAM,QAAQ,KAAK,GAC5B,MAAM,KAAK,GAAG,IAAI,IAAI,gBAAgB,KAAK,UAAU,KAAK,CAAC,EAAE,EAAE;OAC1D,IACL,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,WAEjB,MAAM,KAAK,GAAG,IAAI,IAAI,mBAAmB,KAAK,EAAE,EAAE;CAEtD;CACA,OAAO,MAAM,SAAS,IAAI,MAAM,MAAM,KAAK,GAAG,IAAI;AACpD;AAEA,SAAS,aAAa,KAAsB;CAC1C,IAAI,UAAU,iBAAiB,IAAI,IAAI;CAEvC,IAAI,IAAI,MACN,UAAU,YAA6B,gBAAgB,IAAI,IAAI,EAAE,IAAI,QAAQ;CAE/E,IAAI,IAAI,WACN,UAAU,IAAI,gBAAgB,UAAU,gBAAgB,IAAI,SAAS,EAAE,IAAI,QAAQ,IAAI,gBAAgB;CAEzG,MAAM,YAAsB,CAAC;CAC7B,IAAI,IAAI,OAAO,UAAU,KAAK,UAAU,gBAAgB,IAAI,KAAK,EAAE,EAAE;CACrE,IAAI,IAAI,YACN,UAAU,KAAK,eAAe,gBAAgB,IAAI,UAAU,EAAE,EAAE;CAClE,IAAI,IAAI,kBAAkB,KAAA,GACxB,UAAU,KAAK,kBAAkB,IAAI,cAAc,EAAE;CACvD,IAAI,UAAU,SAAS,GACrB,UAAU,IAAI,gBAAgB,GAAG,UAAU,KAAK,GAAG,EAAE,GAAG,QAAQ,IAAI,gBAAgB;CAGtF,KAAK,IAAI,IAAI,uBAAuB,SAAS,GAAG,KAAK,GAAG,KAAK;EAC3D,MAAM,EAAE,KAAK,aAAa,uBAAuB;EACjD,IAAI,IAAI,WAAW,UAAU,IAAI,IAAI,GAAG,QAAQ,IAAI,IAAI;CAC1D;CACA,OAAO;AACT;AAEA,SAAS,cAAc,MAAyB;CAC9C,OAAO,KAAK,IAAI,YAAY,CAAC,CAAC,KAAK,EAAE;AACvC;AAEA,SAAS,cAAc,MAAe,OAAuB;CAC3D,MAAM,SAAS,KAAK,OAAO,KAAK;CAChC,MAAM,MAAM,gBAAgB,KAAK,IAAI;CACrC,MAAM,MAAM,IAAI;CAChB,MAAM,aAAa;CAEnB,IAAI,IAAI,YAAY,SAAS,gBAAgB;EAC3C,MAAM,WAAY,WAAW,YAA0B,CAAC;EACxD,MAAM,UAAU,eAAe,UAAU;EACzC,IAAI,SAAS,WAAW,GACtB,OAAO,GAAG,OAAO,GAAG,MAAM,QAAQ;EAKpC,OAAO,GAAG,OAAO,GAAG,MAAM,QAAQ,KAHd,SACjB,KAAK,MAAM,cAAc,GAAG,QAAQ,CAAC,CAAC,CAAC,CACvC,KAAK,IACyC,EAAE,IAAI,OAAO,IAAI,IAAI;CACxE;CAEA,IAAI,KAAK,SAAS,OAAO;EACvB,MAAM,aAAc,WAAW,cAAyB;EAExD,OAAO,GAAG,OAAO,GAAG,MADJ,eAAe,UACC,EAAE,KAAK,WAAW,IAAI,OAAO,IAAI,IAAI;CACvE;CAGA,IACE,IAAI,sBACJ,MAAM,QAAQ,WAAW,IAAI,KAC5B,WAAW,KAAmB,SAAS,GACxC;EACA,MAAM,OAAO,WAAW;EAKxB,OAAO,GAAG,OAAO,GAAG,MAFJ,eAAe;GADJ,GAAG;GAAY,MAAM,KAAA;EACF,CAEd,EAAE,GADZ,cAAc,IACa,EAAE,IAAI,IAAI;CAC7D;CAGA,OAAO,GAAG,OAAO,GAAG,MADJ,eAAe,UACC,EAAE;AACpC;;;;;;;;;;;;;;;;AAiBA,SAAgB,aAAa,OAA0B;CACrD,OAAO,MACJ,KAAK,SAAS,YAAY,cAAc,MAAM,CAAC,EAAE,WAAW,CAAC,CAC7D,KAAK,IAAI;AACd"}
@@ -1,8 +1,8 @@
1
+ import { getNodeMetadata } from "../nodeMetadata.js";
1
2
  import { renderArrowNode } from "../../renderPptx/nodes/arrow.js";
2
3
  //#region src/registry/definitions/arrow.ts
3
4
  const arrowNodeDef = {
4
- type: "arrow",
5
- category: "leaf",
5
+ ...getNodeMetadata("arrow"),
6
6
  applyYogaStyle(_node, yn) {
7
7
  yn.setWidth(0);
8
8
  yn.setHeight(0);
@@ -1 +1 @@
1
- {"version":3,"file":"arrow.js","names":["n"],"sources":["../../../src/registry/definitions/arrow.ts"],"sourcesContent":["import type { POMNode } from \"../../types.ts\";\nimport type { NodeDefinition } from \"../types.ts\";\nimport { renderArrowNode } from \"../../renderPptx/nodes/arrow.ts\";\n\nexport const arrowNodeDef: NodeDefinition = {\n type: \"arrow\",\n category: \"leaf\",\n applyYogaStyle(_node, yn) {\n // arrow ノードは ID 参照で位置を決定するため、Yoga レイアウトではサイズ 0 として扱う\n yn.setWidth(0);\n yn.setHeight(0);\n },\n toPositioned(pom, absoluteX, absoluteY) {\n const n = pom as Extract<POMNode, { type: \"arrow\" }>;\n return {\n ...n,\n x: absoluteX,\n y: absoluteY,\n w: 0,\n h: 0,\n };\n },\n render(node, ctx) {\n renderArrowNode(node as Extract<typeof node, { type: \"arrow\" }>, ctx);\n },\n};\n"],"mappings":";;AAIA,MAAa,eAA+B;CAC1C,MAAM;CACN,UAAU;CACV,eAAe,OAAO,IAAI;EAExB,GAAG,SAAS,CAAC;EACb,GAAG,UAAU,CAAC;CAChB;CACA,aAAa,KAAK,WAAW,WAAW;EAEtC,OAAO;GACL,GAAGA;GACH,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG;EACL;CACF;CACA,OAAO,MAAM,KAAK;EAChB,gBAAgB,MAAiD,GAAG;CACtE;AACF"}
1
+ {"version":3,"file":"arrow.js","names":["n"],"sources":["../../../src/registry/definitions/arrow.ts"],"sourcesContent":["import type { POMNode } from \"../../types.ts\";\nimport type { NodeDefinition } from \"../types.ts\";\nimport { renderArrowNode } from \"../../renderPptx/nodes/arrow.ts\";\nimport { getNodeMetadata } from \"../nodeMetadata.ts\";\n\nexport const arrowNodeDef: NodeDefinition = {\n ...getNodeMetadata(\"arrow\"),\n applyYogaStyle(_node, yn) {\n // arrow ノードは ID 参照で位置を決定するため、Yoga レイアウトではサイズ 0 として扱う\n yn.setWidth(0);\n yn.setHeight(0);\n },\n toPositioned(pom, absoluteX, absoluteY) {\n const n = pom as Extract<POMNode, { type: \"arrow\" }>;\n return {\n ...n,\n x: absoluteX,\n y: absoluteY,\n w: 0,\n h: 0,\n };\n },\n render(node, ctx) {\n renderArrowNode(node as Extract<typeof node, { type: \"arrow\" }>, ctx);\n },\n};\n"],"mappings":";;;AAKA,MAAa,eAA+B;CAC1C,GAAG,gBAAgB,OAAO;CAC1B,eAAe,OAAO,IAAI;EAExB,GAAG,SAAS,CAAC;EACb,GAAG,UAAU,CAAC;CAChB;CACA,aAAa,KAAK,WAAW,WAAW;EAEtC,OAAO;GACL,GAAGA;GACH,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG;EACL;CACF;CACA,OAAO,MAAM,KAAK;EAChB,gBAAgB,MAAiD,GAAG;CACtE;AACF"}
@@ -1,8 +1,8 @@
1
+ import { getNodeMetadata } from "../nodeMetadata.js";
1
2
  import { renderChartNode } from "../../renderPptx/nodes/chart.js";
2
3
  //#region src/registry/definitions/chart.ts
3
4
  const chartNodeDef = {
4
- type: "chart",
5
- category: "leaf",
5
+ ...getNodeMetadata("chart"),
6
6
  render(node, ctx) {
7
7
  renderChartNode(node, ctx);
8
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"chart.js","names":[],"sources":["../../../src/registry/definitions/chart.ts"],"sourcesContent":["import type { NodeDefinition } from \"../types.ts\";\nimport { renderChartNode } from \"../../renderPptx/nodes/chart.ts\";\n\nexport const chartNodeDef: NodeDefinition = {\n type: \"chart\",\n category: \"leaf\",\n render(node, ctx) {\n renderChartNode(node as Extract<typeof node, { type: \"chart\" }>, ctx);\n },\n};\n"],"mappings":";;AAGA,MAAa,eAA+B;CAC1C,MAAM;CACN,UAAU;CACV,OAAO,MAAM,KAAK;EAChB,gBAAgB,MAAiD,GAAG;CACtE;AACF"}
1
+ {"version":3,"file":"chart.js","names":[],"sources":["../../../src/registry/definitions/chart.ts"],"sourcesContent":["import type { NodeDefinition } from \"../types.ts\";\nimport { renderChartNode } from \"../../renderPptx/nodes/chart.ts\";\nimport { getNodeMetadata } from \"../nodeMetadata.ts\";\n\nexport const chartNodeDef: NodeDefinition = {\n ...getNodeMetadata(\"chart\"),\n render(node, ctx) {\n renderChartNode(node as Extract<typeof node, { type: \"chart\" }>, ctx);\n },\n};\n"],"mappings":";;;AAIA,MAAa,eAA+B;CAC1C,GAAG,gBAAgB,OAAO;CAC1B,OAAO,MAAM,KAAK;EAChB,gBAAgB,MAAiD,GAAG;CACtE;AACF"}
@@ -1,3 +1,4 @@
1
+ import { getNodeMetadata } from "../nodeMetadata.js";
1
2
  import { measureFlow, measureMatrix, measureProcessArrow, measurePyramid, measureTimeline, measureTree } from "../../calcYogaLayout/measureCompositeNodes.js";
2
3
  import { renderTimelineNode } from "../../renderPptx/nodes/timeline.js";
3
4
  import { renderMatrixNode } from "../../renderPptx/nodes/matrix.js";
@@ -29,48 +30,42 @@ function applyCompositeMeasure(measureFn) {
29
30
  };
30
31
  }
31
32
  const timelineNodeDef = {
32
- type: "timeline",
33
- category: "leaf",
33
+ ...getNodeMetadata("timeline"),
34
34
  applyYogaStyle: applyCompositeMeasure(measureTimeline),
35
35
  render(node, ctx) {
36
36
  renderTimelineNode(node, ctx);
37
37
  }
38
38
  };
39
39
  const matrixNodeDef = {
40
- type: "matrix",
41
- category: "leaf",
40
+ ...getNodeMetadata("matrix"),
42
41
  applyYogaStyle: applyCompositeMeasure(measureMatrix),
43
42
  render(node, ctx) {
44
43
  renderMatrixNode(node, ctx);
45
44
  }
46
45
  };
47
46
  const treeNodeDef = {
48
- type: "tree",
49
- category: "leaf",
47
+ ...getNodeMetadata("tree"),
50
48
  applyYogaStyle: applyCompositeMeasure(measureTree),
51
49
  render(node, ctx) {
52
50
  renderTreeNode(node, ctx);
53
51
  }
54
52
  };
55
53
  const flowNodeDef = {
56
- type: "flow",
57
- category: "leaf",
54
+ ...getNodeMetadata("flow"),
58
55
  applyYogaStyle: applyCompositeMeasure(measureFlow),
59
56
  render(node, ctx) {
60
57
  renderFlowNode(node, ctx);
61
58
  }
62
59
  };
63
60
  const processArrowNodeDef = {
64
- type: "processArrow",
65
- category: "leaf",
61
+ ...getNodeMetadata("processArrow"),
66
62
  applyYogaStyle: applyCompositeMeasure(measureProcessArrow),
67
63
  render(node, ctx) {
68
64
  renderProcessArrowNode(node, ctx);
69
65
  }
70
66
  };
71
67
  const pyramidNodeDef = {
72
- type: "pyramid",
73
- category: "leaf",
68
+ ...getNodeMetadata("pyramid"),
74
69
  applyYogaStyle: applyCompositeMeasure(measurePyramid),
75
70
  render(node, ctx) {
76
71
  renderPyramidNode(node, ctx);
@@ -1 +1 @@
1
- {"version":3,"file":"compositeNodes.js","names":[],"sources":["../../../src/registry/definitions/compositeNodes.ts"],"sourcesContent":["import type { POMNode } from \"../../types.ts\";\nimport type { NodeDefinition, Yoga } from \"../types.ts\";\nimport type { Node as YogaNode } from \"yoga-layout\";\nimport {\n measureProcessArrow,\n measureTimeline,\n measureMatrix,\n measureTree,\n measureFlow,\n measurePyramid,\n} from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { renderTimelineNode } from \"../../renderPptx/nodes/timeline.ts\";\nimport { renderMatrixNode } from \"../../renderPptx/nodes/matrix.ts\";\nimport { renderTreeNode } from \"../../renderPptx/nodes/tree.ts\";\nimport { renderFlowNode } from \"../../renderPptx/nodes/flow.ts\";\nimport { renderProcessArrowNode } from \"../../renderPptx/nodes/processArrow.ts\";\nimport { renderPyramidNode } from \"../../renderPptx/nodes/pyramid.ts\";\n\n/**\n * コンポジットノードの最小スケール閾値。\n * renderPptx/utils/scaleToFit.ts の MIN_SCALE_THRESHOLD と同じ値を維持すること。\n */\nconst MIN_SCALE_THRESHOLD = 0.5;\n\n/** 制約付きサイズを閾値でクランプする */\nfunction constrainWithMinScale(\n intrinsicSize: number,\n availableSize: number,\n): number {\n const minSize = intrinsicSize * MIN_SCALE_THRESHOLD;\n return Math.max(minSize, Math.min(intrinsicSize, availableSize));\n}\n\n/** コンポジットノード共通の measureFunc セットアップ */\nfunction applyCompositeMeasure(\n measureFn: (node: POMNode) => { width: number; height: number },\n): (node: POMNode, yn: YogaNode, yoga: Yoga) => void {\n return (node, yn, yoga) => {\n yn.setMeasureFunc((width, widthMode, height, heightMode) => {\n const intrinsic = measureFn(node);\n return {\n width:\n widthMode !== yoga.MEASURE_MODE_UNDEFINED\n ? constrainWithMinScale(intrinsic.width, width)\n : intrinsic.width,\n height:\n heightMode !== yoga.MEASURE_MODE_UNDEFINED\n ? constrainWithMinScale(intrinsic.height, height)\n : intrinsic.height,\n };\n });\n };\n}\n\nexport const timelineNodeDef: NodeDefinition = {\n type: \"timeline\",\n category: \"leaf\",\n applyYogaStyle: applyCompositeMeasure(\n measureTimeline as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderTimelineNode(node as Extract<typeof node, { type: \"timeline\" }>, ctx);\n },\n};\n\nexport const matrixNodeDef: NodeDefinition = {\n type: \"matrix\",\n category: \"leaf\",\n applyYogaStyle: applyCompositeMeasure(\n measureMatrix as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderMatrixNode(node as Extract<typeof node, { type: \"matrix\" }>, ctx);\n },\n};\n\nexport const treeNodeDef: NodeDefinition = {\n type: \"tree\",\n category: \"leaf\",\n applyYogaStyle: applyCompositeMeasure(\n measureTree as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderTreeNode(node as Extract<typeof node, { type: \"tree\" }>, ctx);\n },\n};\n\nexport const flowNodeDef: NodeDefinition = {\n type: \"flow\",\n category: \"leaf\",\n applyYogaStyle: applyCompositeMeasure(\n measureFlow as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderFlowNode(node as Extract<typeof node, { type: \"flow\" }>, ctx);\n },\n};\n\nexport const processArrowNodeDef: NodeDefinition = {\n type: \"processArrow\",\n category: \"leaf\",\n applyYogaStyle: applyCompositeMeasure(\n measureProcessArrow as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderProcessArrowNode(\n node as Extract<typeof node, { type: \"processArrow\" }>,\n ctx,\n );\n },\n};\n\nexport const pyramidNodeDef: NodeDefinition = {\n type: \"pyramid\",\n category: \"leaf\",\n applyYogaStyle: applyCompositeMeasure(\n measurePyramid as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderPyramidNode(node as Extract<typeof node, { type: \"pyramid\" }>, ctx);\n },\n};\n"],"mappings":";;;;;;;;;;;;AAsBA,MAAM,sBAAsB;;AAG5B,SAAS,sBACP,eACA,eACQ;CACR,MAAM,UAAU,gBAAgB;CAChC,OAAO,KAAK,IAAI,SAAS,KAAK,IAAI,eAAe,aAAa,CAAC;AACjE;;AAGA,SAAS,sBACP,WACmD;CACnD,QAAQ,MAAM,IAAI,SAAS;EACzB,GAAG,gBAAgB,OAAO,WAAW,QAAQ,eAAe;GAC1D,MAAM,YAAY,UAAU,IAAI;GAChC,OAAO;IACL,OACE,cAAc,KAAK,yBACf,sBAAsB,UAAU,OAAO,KAAK,IAC5C,UAAU;IAChB,QACE,eAAe,KAAK,yBAChB,sBAAsB,UAAU,QAAQ,MAAM,IAC9C,UAAU;GAClB;EACF,CAAC;CACH;AACF;AAEA,MAAa,kBAAkC;CAC7C,MAAM;CACN,UAAU;CACV,gBAAgB,sBACd,eACF;CACA,OAAO,MAAM,KAAK;EAChB,mBAAmB,MAAoD,GAAG;CAC5E;AACF;AAEA,MAAa,gBAAgC;CAC3C,MAAM;CACN,UAAU;CACV,gBAAgB,sBACd,aACF;CACA,OAAO,MAAM,KAAK;EAChB,iBAAiB,MAAkD,GAAG;CACxE;AACF;AAEA,MAAa,cAA8B;CACzC,MAAM;CACN,UAAU;CACV,gBAAgB,sBACd,WACF;CACA,OAAO,MAAM,KAAK;EAChB,eAAe,MAAgD,GAAG;CACpE;AACF;AAEA,MAAa,cAA8B;CACzC,MAAM;CACN,UAAU;CACV,gBAAgB,sBACd,WACF;CACA,OAAO,MAAM,KAAK;EAChB,eAAe,MAAgD,GAAG;CACpE;AACF;AAEA,MAAa,sBAAsC;CACjD,MAAM;CACN,UAAU;CACV,gBAAgB,sBACd,mBACF;CACA,OAAO,MAAM,KAAK;EAChB,uBACE,MACA,GACF;CACF;AACF;AAEA,MAAa,iBAAiC;CAC5C,MAAM;CACN,UAAU;CACV,gBAAgB,sBACd,cACF;CACA,OAAO,MAAM,KAAK;EAChB,kBAAkB,MAAmD,GAAG;CAC1E;AACF"}
1
+ {"version":3,"file":"compositeNodes.js","names":[],"sources":["../../../src/registry/definitions/compositeNodes.ts"],"sourcesContent":["import { type POMNode } from \"../../types.ts\";\nimport type { NodeDefinition, Yoga } from \"../types.ts\";\nimport type { Node as YogaNode } from \"yoga-layout\";\nimport {\n measureProcessArrow,\n measureTimeline,\n measureMatrix,\n measureTree,\n measureFlow,\n measurePyramid,\n} from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { renderTimelineNode } from \"../../renderPptx/nodes/timeline.ts\";\nimport { renderMatrixNode } from \"../../renderPptx/nodes/matrix.ts\";\nimport { renderTreeNode } from \"../../renderPptx/nodes/tree.ts\";\nimport { renderFlowNode } from \"../../renderPptx/nodes/flow.ts\";\nimport { renderProcessArrowNode } from \"../../renderPptx/nodes/processArrow.ts\";\nimport { renderPyramidNode } from \"../../renderPptx/nodes/pyramid.ts\";\nimport { getNodeMetadata } from \"../nodeMetadata.ts\";\n\n/**\n * コンポジットノードの最小スケール閾値。\n * renderPptx/utils/scaleToFit.ts の MIN_SCALE_THRESHOLD と同じ値を維持すること。\n */\nconst MIN_SCALE_THRESHOLD = 0.5;\n\n/** 制約付きサイズを閾値でクランプする */\nfunction constrainWithMinScale(\n intrinsicSize: number,\n availableSize: number,\n): number {\n const minSize = intrinsicSize * MIN_SCALE_THRESHOLD;\n return Math.max(minSize, Math.min(intrinsicSize, availableSize));\n}\n\n/** コンポジットノード共通の measureFunc セットアップ */\nfunction applyCompositeMeasure(\n measureFn: (node: POMNode) => { width: number; height: number },\n): (node: POMNode, yn: YogaNode, yoga: Yoga) => void {\n return (node, yn, yoga) => {\n yn.setMeasureFunc((width, widthMode, height, heightMode) => {\n const intrinsic = measureFn(node);\n return {\n width:\n widthMode !== yoga.MEASURE_MODE_UNDEFINED\n ? constrainWithMinScale(intrinsic.width, width)\n : intrinsic.width,\n height:\n heightMode !== yoga.MEASURE_MODE_UNDEFINED\n ? constrainWithMinScale(intrinsic.height, height)\n : intrinsic.height,\n };\n });\n };\n}\n\nexport const timelineNodeDef: NodeDefinition = {\n ...getNodeMetadata(\"timeline\"),\n applyYogaStyle: applyCompositeMeasure(\n measureTimeline as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderTimelineNode(node as Extract<typeof node, { type: \"timeline\" }>, ctx);\n },\n};\n\nexport const matrixNodeDef: NodeDefinition = {\n ...getNodeMetadata(\"matrix\"),\n applyYogaStyle: applyCompositeMeasure(\n measureMatrix as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderMatrixNode(node as Extract<typeof node, { type: \"matrix\" }>, ctx);\n },\n};\n\nexport const treeNodeDef: NodeDefinition = {\n ...getNodeMetadata(\"tree\"),\n applyYogaStyle: applyCompositeMeasure(\n measureTree as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderTreeNode(node as Extract<typeof node, { type: \"tree\" }>, ctx);\n },\n};\n\nexport const flowNodeDef: NodeDefinition = {\n ...getNodeMetadata(\"flow\"),\n applyYogaStyle: applyCompositeMeasure(\n measureFlow as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderFlowNode(node as Extract<typeof node, { type: \"flow\" }>, ctx);\n },\n};\n\nexport const processArrowNodeDef: NodeDefinition = {\n ...getNodeMetadata(\"processArrow\"),\n applyYogaStyle: applyCompositeMeasure(\n measureProcessArrow as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderProcessArrowNode(\n node as Extract<typeof node, { type: \"processArrow\" }>,\n ctx,\n );\n },\n};\n\nexport const pyramidNodeDef: NodeDefinition = {\n ...getNodeMetadata(\"pyramid\"),\n applyYogaStyle: applyCompositeMeasure(\n measurePyramid as (node: POMNode) => { width: number; height: number },\n ),\n render(node, ctx) {\n renderPyramidNode(node as Extract<typeof node, { type: \"pyramid\" }>, ctx);\n },\n};\n"],"mappings":";;;;;;;;;;;;;AAuBA,MAAM,sBAAsB;;AAG5B,SAAS,sBACP,eACA,eACQ;CACR,MAAM,UAAU,gBAAgB;CAChC,OAAO,KAAK,IAAI,SAAS,KAAK,IAAI,eAAe,aAAa,CAAC;AACjE;;AAGA,SAAS,sBACP,WACmD;CACnD,QAAQ,MAAM,IAAI,SAAS;EACzB,GAAG,gBAAgB,OAAO,WAAW,QAAQ,eAAe;GAC1D,MAAM,YAAY,UAAU,IAAI;GAChC,OAAO;IACL,OACE,cAAc,KAAK,yBACf,sBAAsB,UAAU,OAAO,KAAK,IAC5C,UAAU;IAChB,QACE,eAAe,KAAK,yBAChB,sBAAsB,UAAU,QAAQ,MAAM,IAC9C,UAAU;GAClB;EACF,CAAC;CACH;AACF;AAEA,MAAa,kBAAkC;CAC7C,GAAG,gBAAgB,UAAU;CAC7B,gBAAgB,sBACd,eACF;CACA,OAAO,MAAM,KAAK;EAChB,mBAAmB,MAAoD,GAAG;CAC5E;AACF;AAEA,MAAa,gBAAgC;CAC3C,GAAG,gBAAgB,QAAQ;CAC3B,gBAAgB,sBACd,aACF;CACA,OAAO,MAAM,KAAK;EAChB,iBAAiB,MAAkD,GAAG;CACxE;AACF;AAEA,MAAa,cAA8B;CACzC,GAAG,gBAAgB,MAAM;CACzB,gBAAgB,sBACd,WACF;CACA,OAAO,MAAM,KAAK;EAChB,eAAe,MAAgD,GAAG;CACpE;AACF;AAEA,MAAa,cAA8B;CACzC,GAAG,gBAAgB,MAAM;CACzB,gBAAgB,sBACd,WACF;CACA,OAAO,MAAM,KAAK;EAChB,eAAe,MAAgD,GAAG;CACpE;AACF;AAEA,MAAa,sBAAsC;CACjD,GAAG,gBAAgB,cAAc;CACjC,gBAAgB,sBACd,mBACF;CACA,OAAO,MAAM,KAAK;EAChB,uBACE,MACA,GACF;CACF;AACF;AAEA,MAAa,iBAAiC;CAC5C,GAAG,gBAAgB,SAAS;CAC5B,gBAAgB,sBACd,cACF;CACA,OAAO,MAAM,KAAK;EAChB,kBAAkB,MAAmD,GAAG;CAC1E;AACF"}
@@ -1,11 +1,11 @@
1
1
  import { getContentArea } from "../../renderPptx/utils/contentArea.js";
2
+ import { getNodeMetadata } from "../nodeMetadata.js";
2
3
  import { rasterizeIcon } from "../../icons/renderIcon.js";
3
4
  import "../../icons/index.js";
4
5
  import { renderIconNode } from "../../renderPptx/nodes/icon.js";
5
6
  //#region src/registry/definitions/icon.ts
6
7
  const iconNodeDef = {
7
- type: "icon",
8
- category: "leaf",
8
+ ...getNodeMetadata("icon"),
9
9
  applyYogaStyle(node, yn) {
10
10
  const n = node;
11
11
  const iconSize = n.size ?? 24;
@@ -1 +1 @@
1
- {"version":3,"file":"icon.js","names":[],"sources":["../../../src/registry/definitions/icon.ts"],"sourcesContent":["import type { POMNode, PositionedNode } from \"../../types.ts\";\nimport type { NodeDefinition } from \"../types.ts\";\nimport { rasterizeIcon } from \"../../icons/index.ts\";\nimport { renderIconNode } from \"../../renderPptx/nodes/icon.ts\";\nimport { getContentArea } from \"../../renderPptx/utils/contentArea.ts\";\n\nexport const iconNodeDef: NodeDefinition = {\n type: \"icon\",\n category: \"leaf\",\n applyYogaStyle(node, yn) {\n const n = node as Extract<POMNode, { type: \"icon\" }>;\n const iconSize = n.size ?? 24;\n // variant 指定時はアイコンサイズ + パディング分を全体サイズとする\n const totalSize = n.variant ? Math.ceil(iconSize * 1.75) : iconSize;\n yn.setMeasureFunc(() => ({ width: totalSize, height: totalSize }));\n },\n async toPositioned(pom, absoluteX, absoluteY, layout, ctx) {\n const n = pom as Extract<POMNode, { type: \"icon\" }>;\n const iconSize = n.size ?? 24;\n\n // padding を考慮したコンテンツ領域で bg/icon の座標を計算\n const content = getContentArea({\n x: absoluteX,\n y: absoluteY,\n w: layout.width,\n h: layout.height,\n padding: n.padding,\n });\n\n // 実描画サイズに合わせてラスタライズ(不要に大きい PNG を防ぐ)\n const rasterSize = Math.max(\n Math.ceil(n.variant ? iconSize : Math.min(content.w, content.h)),\n iconSize,\n );\n const iconImageData = await rasterizeIcon(\n n.name,\n rasterSize,\n n.color ?? \"#000000\",\n ctx.iconRasterCache,\n );\n\n const positioned: Record<string, unknown> = {\n ...n,\n x: absoluteX,\n y: absoluteY,\n w: layout.width,\n h: layout.height,\n iconImageData,\n };\n\n if (n.variant) {\n const totalSize = Math.ceil(iconSize * 1.75);\n // 背景図形は totalSize の正方形として、コンテンツ領域の中央に配置\n positioned.bgX = content.x + (content.w - totalSize) / 2;\n positioned.bgY = content.y + (content.h - totalSize) / 2;\n positioned.bgW = totalSize;\n positioned.bgH = totalSize;\n // アイコンはコンテンツ領域の中央に配置\n positioned.iconX = content.x + (content.w - iconSize) / 2;\n positioned.iconY = content.y + (content.h - iconSize) / 2;\n positioned.iconW = iconSize;\n positioned.iconH = iconSize;\n } else {\n // variant なしの場合もアスペクト比を維持し、コンテンツ領域の中央に配置\n const iconSide = Math.min(content.w, content.h);\n positioned.iconX = content.x + (content.w - iconSide) / 2;\n positioned.iconY = content.y + (content.h - iconSide) / 2;\n positioned.iconW = iconSide;\n positioned.iconH = iconSide;\n }\n return positioned as PositionedNode;\n },\n render(node, ctx) {\n renderIconNode(node as Extract<typeof node, { type: \"icon\" }>, ctx);\n },\n};\n"],"mappings":";;;;;AAMA,MAAa,cAA8B;CACzC,MAAM;CACN,UAAU;CACV,eAAe,MAAM,IAAI;EACvB,MAAM,IAAI;EACV,MAAM,WAAW,EAAE,QAAQ;EAE3B,MAAM,YAAY,EAAE,UAAU,KAAK,KAAK,WAAW,IAAI,IAAI;EAC3D,GAAG,sBAAsB;GAAE,OAAO;GAAW,QAAQ;EAAU,EAAE;CACnE;CACA,MAAM,aAAa,KAAK,WAAW,WAAW,QAAQ,KAAK;EACzD,MAAM,IAAI;EACV,MAAM,WAAW,EAAE,QAAQ;EAG3B,MAAM,UAAU,eAAe;GAC7B,GAAG;GACH,GAAG;GACH,GAAG,OAAO;GACV,GAAG,OAAO;GACV,SAAS,EAAE;EACb,CAAC;EAGD,MAAM,aAAa,KAAK,IACtB,KAAK,KAAK,EAAE,UAAU,WAAW,KAAK,IAAI,QAAQ,GAAG,QAAQ,CAAC,CAAC,GAC/D,QACF;EACA,MAAM,gBAAgB,MAAM,cAC1B,EAAE,MACF,YACA,EAAE,SAAS,WACX,IAAI,eACN;EAEA,MAAM,aAAsC;GAC1C,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG,OAAO;GACV,GAAG,OAAO;GACV;EACF;EAEA,IAAI,EAAE,SAAS;GACb,MAAM,YAAY,KAAK,KAAK,WAAW,IAAI;GAE3C,WAAW,MAAM,QAAQ,KAAK,QAAQ,IAAI,aAAa;GACvD,WAAW,MAAM,QAAQ,KAAK,QAAQ,IAAI,aAAa;GACvD,WAAW,MAAM;GACjB,WAAW,MAAM;GAEjB,WAAW,QAAQ,QAAQ,KAAK,QAAQ,IAAI,YAAY;GACxD,WAAW,QAAQ,QAAQ,KAAK,QAAQ,IAAI,YAAY;GACxD,WAAW,QAAQ;GACnB,WAAW,QAAQ;EACrB,OAAO;GAEL,MAAM,WAAW,KAAK,IAAI,QAAQ,GAAG,QAAQ,CAAC;GAC9C,WAAW,QAAQ,QAAQ,KAAK,QAAQ,IAAI,YAAY;GACxD,WAAW,QAAQ,QAAQ,KAAK,QAAQ,IAAI,YAAY;GACxD,WAAW,QAAQ;GACnB,WAAW,QAAQ;EACrB;EACA,OAAO;CACT;CACA,OAAO,MAAM,KAAK;EAChB,eAAe,MAAgD,GAAG;CACpE;AACF"}
1
+ {"version":3,"file":"icon.js","names":[],"sources":["../../../src/registry/definitions/icon.ts"],"sourcesContent":["import { type POMNode, type PositionedNode } from \"../../types.ts\";\nimport type { NodeDefinition } from \"../types.ts\";\nimport { rasterizeIcon } from \"../../icons/index.ts\";\nimport { renderIconNode } from \"../../renderPptx/nodes/icon.ts\";\nimport { getContentArea } from \"../../renderPptx/utils/contentArea.ts\";\nimport { getNodeMetadata } from \"../nodeMetadata.ts\";\n\nexport const iconNodeDef: NodeDefinition = {\n ...getNodeMetadata(\"icon\"),\n applyYogaStyle(node, yn) {\n const n = node as Extract<POMNode, { type: \"icon\" }>;\n const iconSize = n.size ?? 24;\n // variant 指定時はアイコンサイズ + パディング分を全体サイズとする\n const totalSize = n.variant ? Math.ceil(iconSize * 1.75) : iconSize;\n yn.setMeasureFunc(() => ({ width: totalSize, height: totalSize }));\n },\n async toPositioned(pom, absoluteX, absoluteY, layout, ctx) {\n const n = pom as Extract<POMNode, { type: \"icon\" }>;\n const iconSize = n.size ?? 24;\n\n // padding を考慮したコンテンツ領域で bg/icon の座標を計算\n const content = getContentArea({\n x: absoluteX,\n y: absoluteY,\n w: layout.width,\n h: layout.height,\n padding: n.padding,\n });\n\n // 実描画サイズに合わせてラスタライズ(不要に大きい PNG を防ぐ)\n const rasterSize = Math.max(\n Math.ceil(n.variant ? iconSize : Math.min(content.w, content.h)),\n iconSize,\n );\n const iconImageData = await rasterizeIcon(\n n.name,\n rasterSize,\n n.color ?? \"#000000\",\n ctx.iconRasterCache,\n );\n\n const positioned: Record<string, unknown> = {\n ...n,\n x: absoluteX,\n y: absoluteY,\n w: layout.width,\n h: layout.height,\n iconImageData,\n };\n\n if (n.variant) {\n const totalSize = Math.ceil(iconSize * 1.75);\n // 背景図形は totalSize の正方形として、コンテンツ領域の中央に配置\n positioned.bgX = content.x + (content.w - totalSize) / 2;\n positioned.bgY = content.y + (content.h - totalSize) / 2;\n positioned.bgW = totalSize;\n positioned.bgH = totalSize;\n // アイコンはコンテンツ領域の中央に配置\n positioned.iconX = content.x + (content.w - iconSize) / 2;\n positioned.iconY = content.y + (content.h - iconSize) / 2;\n positioned.iconW = iconSize;\n positioned.iconH = iconSize;\n } else {\n // variant なしの場合もアスペクト比を維持し、コンテンツ領域の中央に配置\n const iconSide = Math.min(content.w, content.h);\n positioned.iconX = content.x + (content.w - iconSide) / 2;\n positioned.iconY = content.y + (content.h - iconSide) / 2;\n positioned.iconW = iconSide;\n positioned.iconH = iconSide;\n }\n return positioned as PositionedNode;\n },\n render(node, ctx) {\n renderIconNode(node as Extract<typeof node, { type: \"icon\" }>, ctx);\n },\n};\n"],"mappings":";;;;;;AAOA,MAAa,cAA8B;CACzC,GAAG,gBAAgB,MAAM;CACzB,eAAe,MAAM,IAAI;EACvB,MAAM,IAAI;EACV,MAAM,WAAW,EAAE,QAAQ;EAE3B,MAAM,YAAY,EAAE,UAAU,KAAK,KAAK,WAAW,IAAI,IAAI;EAC3D,GAAG,sBAAsB;GAAE,OAAO;GAAW,QAAQ;EAAU,EAAE;CACnE;CACA,MAAM,aAAa,KAAK,WAAW,WAAW,QAAQ,KAAK;EACzD,MAAM,IAAI;EACV,MAAM,WAAW,EAAE,QAAQ;EAG3B,MAAM,UAAU,eAAe;GAC7B,GAAG;GACH,GAAG;GACH,GAAG,OAAO;GACV,GAAG,OAAO;GACV,SAAS,EAAE;EACb,CAAC;EAGD,MAAM,aAAa,KAAK,IACtB,KAAK,KAAK,EAAE,UAAU,WAAW,KAAK,IAAI,QAAQ,GAAG,QAAQ,CAAC,CAAC,GAC/D,QACF;EACA,MAAM,gBAAgB,MAAM,cAC1B,EAAE,MACF,YACA,EAAE,SAAS,WACX,IAAI,eACN;EAEA,MAAM,aAAsC;GAC1C,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG,OAAO;GACV,GAAG,OAAO;GACV;EACF;EAEA,IAAI,EAAE,SAAS;GACb,MAAM,YAAY,KAAK,KAAK,WAAW,IAAI;GAE3C,WAAW,MAAM,QAAQ,KAAK,QAAQ,IAAI,aAAa;GACvD,WAAW,MAAM,QAAQ,KAAK,QAAQ,IAAI,aAAa;GACvD,WAAW,MAAM;GACjB,WAAW,MAAM;GAEjB,WAAW,QAAQ,QAAQ,KAAK,QAAQ,IAAI,YAAY;GACxD,WAAW,QAAQ,QAAQ,KAAK,QAAQ,IAAI,YAAY;GACxD,WAAW,QAAQ;GACnB,WAAW,QAAQ;EACrB,OAAO;GAEL,MAAM,WAAW,KAAK,IAAI,QAAQ,GAAG,QAAQ,CAAC;GAC9C,WAAW,QAAQ,QAAQ,KAAK,QAAQ,IAAI,YAAY;GACxD,WAAW,QAAQ,QAAQ,KAAK,QAAQ,IAAI,YAAY;GACxD,WAAW,QAAQ;GACnB,WAAW,QAAQ;EACrB;EACA,OAAO;CACT;CACA,OAAO,MAAM,KAAK;EAChB,eAAe,MAAgD,GAAG;CACpE;AACF"}
@@ -1,9 +1,9 @@
1
1
  import { getImageData, measureImage } from "../../shared/measureImage.js";
2
+ import { getNodeMetadata } from "../nodeMetadata.js";
2
3
  import { renderImageNode } from "../../renderPptx/nodes/image.js";
3
4
  //#region src/registry/definitions/image.ts
4
5
  const imageNodeDef = {
5
- type: "image",
6
- category: "leaf",
6
+ ...getNodeMetadata("image"),
7
7
  applyYogaStyle(node, yn, _yoga, ctx) {
8
8
  const src = node.src;
9
9
  yn.setMeasureFunc(() => {