@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
@@ -0,0 +1,103 @@
1
+ //#region src/shared/gradient.ts
2
+ /** `to <方向>` キーワード → CSS 角度 (deg)。コーナーは 45 度刻みの近似 */
3
+ const DIRECTION_KEYWORDS = {
4
+ "to top": 0,
5
+ "to right": 90,
6
+ "to bottom": 180,
7
+ "to left": 270,
8
+ "to top right": 45,
9
+ "to right top": 45,
10
+ "to bottom right": 135,
11
+ "to right bottom": 135,
12
+ "to bottom left": 225,
13
+ "to left bottom": 225,
14
+ "to top left": 315,
15
+ "to left top": 315
16
+ };
17
+ const COLOR_PATTERN = /^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/;
18
+ function normalizeColor(raw) {
19
+ const match = COLOR_PATTERN.exec(raw);
20
+ if (!match) return null;
21
+ const hex = match[1];
22
+ if (hex.length === 3) return hex.split("").map((c) => c + c).join("").toUpperCase();
23
+ return hex.toUpperCase();
24
+ }
25
+ /**
26
+ * 位置省略されたストップに CSS 互換のルールで位置を割り当てる。
27
+ * - 先頭が省略なら 0、末尾が省略なら 100
28
+ * - 中間の省略は前後の明示位置の間で均等配置
29
+ * - 位置が前のストップより小さい場合は前の値に切り上げ (非減少を保証)
30
+ */
31
+ function resolveStopPositions(stops) {
32
+ const positions = stops.map((s) => s.position);
33
+ if (positions[0] === void 0) positions[0] = 0;
34
+ if (positions[positions.length - 1] === void 0) positions[positions.length - 1] = 100;
35
+ let prevIndex = 0;
36
+ for (let i = 1; i < positions.length; i++) {
37
+ if (positions[i] === void 0) continue;
38
+ const gap = i - prevIndex;
39
+ if (gap > 1) {
40
+ const start = positions[prevIndex];
41
+ const end = positions[i];
42
+ for (let j = 1; j < gap; j++) positions[prevIndex + j] = start + (end - start) * j / gap;
43
+ }
44
+ prevIndex = i;
45
+ }
46
+ let prev = 0;
47
+ return stops.map((stop, i) => {
48
+ const clamped = Math.min(Math.max(positions[i], 0), 100);
49
+ const position = Math.max(clamped, prev);
50
+ prev = position;
51
+ return {
52
+ color: stop.color,
53
+ position
54
+ };
55
+ });
56
+ }
57
+ /**
58
+ * linear-gradient() 構文をパースする。不正な構文の場合は null を返す。
59
+ */
60
+ function parseLinearGradient(value) {
61
+ const match = /^\s*linear-gradient\s*\(\s*(.+?)\s*\)\s*$/.exec(value);
62
+ if (!match) return null;
63
+ const args = match[1].split(",").map((s) => s.trim());
64
+ if (args.length === 0) return null;
65
+ let angle = 180;
66
+ let stopArgs = args;
67
+ const first = args[0];
68
+ const angleMatch = /^(-?\d+(?:\.\d+)?)deg$/.exec(first);
69
+ const directionAngle = DIRECTION_KEYWORDS[first.toLowerCase().replace(/\s+/g, " ")];
70
+ if (angleMatch) {
71
+ angle = (Number(angleMatch[1]) % 360 + 360) % 360;
72
+ stopArgs = args.slice(1);
73
+ } else if (directionAngle !== void 0) {
74
+ angle = directionAngle;
75
+ stopArgs = args.slice(1);
76
+ }
77
+ if (stopArgs.length < 2) return null;
78
+ const stops = [];
79
+ for (const stopArg of stopArgs) {
80
+ const parts = stopArg.split(/\s+/);
81
+ if (parts.length === 0 || parts.length > 2) return null;
82
+ const color = normalizeColor(parts[0]);
83
+ if (color === null) return null;
84
+ let position;
85
+ if (parts.length === 2) {
86
+ const posMatch = /^(-?\d+(?:\.\d+)?)%$/.exec(parts[1]);
87
+ if (!posMatch) return null;
88
+ position = Number(posMatch[1]);
89
+ }
90
+ stops.push({
91
+ color,
92
+ position
93
+ });
94
+ }
95
+ return {
96
+ angle,
97
+ stops: resolveStopPositions(stops)
98
+ };
99
+ }
100
+ //#endregion
101
+ export { parseLinearGradient };
102
+
103
+ //# sourceMappingURL=gradient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gradient.js","names":[],"sources":["../../src/shared/gradient.ts"],"sourcesContent":["/**\n * リニアグラデーション文字列のパース\n *\n * CSS の linear-gradient() 風の構文を受け付ける:\n * linear-gradient(135deg, #FF0000 0%, #0000FF 100%)\n * linear-gradient(to right, #FF0000, #00FF00, #0000FF)\n * linear-gradient(#FF0000, #0000FF) ← 角度省略時は 180deg (上→下)\n *\n * - 角度: `<数値>deg` または `to <方向>` キーワード。省略時は 180deg。\n * - カラーストップ: 16進カラー (#RGB / #RRGGBB / RRGGBB) + 任意の位置 (%)。\n * 位置省略時は CSS と同様に補間する (先頭 0% / 末尾 100% / 中間は線形補間)。\n * - ストップは 2 つ以上必須。\n */\n\nexport interface GradientStop {\n /** 6桁大文字 HEX (# なし) */\n color: string;\n /** 0-100 (%) */\n position: number;\n}\n\nexport interface LinearGradient {\n /** CSS 基準の角度 (deg)。0 = 上向き、時計回り。0-360 に正規化済み */\n angle: number;\n stops: GradientStop[];\n}\n\n/** `to <方向>` キーワード → CSS 角度 (deg)。コーナーは 45 度刻みの近似 */\nconst DIRECTION_KEYWORDS: Record<string, number> = {\n \"to top\": 0,\n \"to right\": 90,\n \"to bottom\": 180,\n \"to left\": 270,\n \"to top right\": 45,\n \"to right top\": 45,\n \"to bottom right\": 135,\n \"to right bottom\": 135,\n \"to bottom left\": 225,\n \"to left bottom\": 225,\n \"to top left\": 315,\n \"to left top\": 315,\n};\n\nconst COLOR_PATTERN = /^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/;\n\nfunction normalizeColor(raw: string): string | null {\n const match = COLOR_PATTERN.exec(raw);\n if (!match) return null;\n const hex = match[1];\n if (hex.length === 3) {\n return hex\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n .toUpperCase();\n }\n return hex.toUpperCase();\n}\n\n/**\n * 位置省略されたストップに CSS 互換のルールで位置を割り当てる。\n * - 先頭が省略なら 0、末尾が省略なら 100\n * - 中間の省略は前後の明示位置の間で均等配置\n * - 位置が前のストップより小さい場合は前の値に切り上げ (非減少を保証)\n */\nfunction resolveStopPositions(\n stops: { color: string; position: number | undefined }[],\n): GradientStop[] {\n const positions: (number | undefined)[] = stops.map((s) => s.position);\n if (positions[0] === undefined) positions[0] = 0;\n if (positions[positions.length - 1] === undefined) {\n positions[positions.length - 1] = 100;\n }\n\n let prevIndex = 0;\n for (let i = 1; i < positions.length; i++) {\n if (positions[i] === undefined) continue;\n const gap = i - prevIndex;\n if (gap > 1) {\n const start = positions[prevIndex]!;\n const end = positions[i]!;\n for (let j = 1; j < gap; j++) {\n positions[prevIndex + j] = start + ((end - start) * j) / gap;\n }\n }\n prevIndex = i;\n }\n\n let prev = 0;\n return stops.map((stop, i) => {\n const clamped = Math.min(Math.max(positions[i]!, 0), 100);\n const position = Math.max(clamped, prev);\n prev = position;\n return { color: stop.color, position };\n });\n}\n\n/**\n * linear-gradient() 構文をパースする。不正な構文の場合は null を返す。\n */\nexport function parseLinearGradient(value: string): LinearGradient | null {\n const match = /^\\s*linear-gradient\\s*\\(\\s*(.+?)\\s*\\)\\s*$/.exec(value);\n if (!match) return null;\n\n const args = match[1].split(\",\").map((s) => s.trim());\n if (args.length === 0) return null;\n\n let angle = 180;\n let stopArgs = args;\n\n const first = args[0];\n const angleMatch = /^(-?\\d+(?:\\.\\d+)?)deg$/.exec(first);\n const directionAngle =\n DIRECTION_KEYWORDS[first.toLowerCase().replace(/\\s+/g, \" \")];\n if (angleMatch) {\n angle = ((Number(angleMatch[1]) % 360) + 360) % 360;\n stopArgs = args.slice(1);\n } else if (directionAngle !== undefined) {\n angle = directionAngle;\n stopArgs = args.slice(1);\n }\n\n if (stopArgs.length < 2) return null;\n\n const stops: { color: string; position: number | undefined }[] = [];\n for (const stopArg of stopArgs) {\n const parts = stopArg.split(/\\s+/);\n if (parts.length === 0 || parts.length > 2) return null;\n const color = normalizeColor(parts[0]);\n if (color === null) return null;\n\n let position: number | undefined;\n if (parts.length === 2) {\n const posMatch = /^(-?\\d+(?:\\.\\d+)?)%$/.exec(parts[1]);\n if (!posMatch) return null;\n position = Number(posMatch[1]);\n }\n stops.push({ color, position });\n }\n\n return { angle, stops: resolveStopPositions(stops) };\n}\n"],"mappings":";;AA4BA,MAAM,qBAA6C;CACjD,UAAU;CACV,YAAY;CACZ,aAAa;CACb,WAAW;CACX,gBAAgB;CAChB,gBAAgB;CAChB,mBAAmB;CACnB,mBAAmB;CACnB,kBAAkB;CAClB,kBAAkB;CAClB,eAAe;CACf,eAAe;AACjB;AAEA,MAAM,gBAAgB;AAEtB,SAAS,eAAe,KAA4B;CAClD,MAAM,QAAQ,cAAc,KAAK,GAAG;CACpC,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,MAAM,MAAM;CAClB,IAAI,IAAI,WAAW,GACjB,OAAO,IACJ,MAAM,EAAE,CAAC,CACT,KAAK,MAAM,IAAI,CAAC,CAAC,CACjB,KAAK,EAAE,CAAC,CACR,YAAY;CAEjB,OAAO,IAAI,YAAY;AACzB;;;;;;;AAQA,SAAS,qBACP,OACgB;CAChB,MAAM,YAAoC,MAAM,KAAK,MAAM,EAAE,QAAQ;CACrE,IAAI,UAAU,OAAO,KAAA,GAAW,UAAU,KAAK;CAC/C,IAAI,UAAU,UAAU,SAAS,OAAO,KAAA,GACtC,UAAU,UAAU,SAAS,KAAK;CAGpC,IAAI,YAAY;CAChB,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;EACzC,IAAI,UAAU,OAAO,KAAA,GAAW;EAChC,MAAM,MAAM,IAAI;EAChB,IAAI,MAAM,GAAG;GACX,MAAM,QAAQ,UAAU;GACxB,MAAM,MAAM,UAAU;GACtB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,KACvB,UAAU,YAAY,KAAK,SAAU,MAAM,SAAS,IAAK;EAE7D;EACA,YAAY;CACd;CAEA,IAAI,OAAO;CACX,OAAO,MAAM,KAAK,MAAM,MAAM;EAC5B,MAAM,UAAU,KAAK,IAAI,KAAK,IAAI,UAAU,IAAK,CAAC,GAAG,GAAG;EACxD,MAAM,WAAW,KAAK,IAAI,SAAS,IAAI;EACvC,OAAO;EACP,OAAO;GAAE,OAAO,KAAK;GAAO;EAAS;CACvC,CAAC;AACH;;;;AAKA,SAAgB,oBAAoB,OAAsC;CACxE,MAAM,QAAQ,4CAA4C,KAAK,KAAK;CACpE,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,OAAO,MAAM,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC;CACpD,IAAI,KAAK,WAAW,GAAG,OAAO;CAE9B,IAAI,QAAQ;CACZ,IAAI,WAAW;CAEf,MAAM,QAAQ,KAAK;CACnB,MAAM,aAAa,yBAAyB,KAAK,KAAK;CACtD,MAAM,iBACJ,mBAAmB,MAAM,YAAY,CAAC,CAAC,QAAQ,QAAQ,GAAG;CAC5D,IAAI,YAAY;EACd,SAAU,OAAO,WAAW,EAAE,IAAI,MAAO,OAAO;EAChD,WAAW,KAAK,MAAM,CAAC;CACzB,OAAO,IAAI,mBAAmB,KAAA,GAAW;EACvC,QAAQ;EACR,WAAW,KAAK,MAAM,CAAC;CACzB;CAEA,IAAI,SAAS,SAAS,GAAG,OAAO;CAEhC,MAAM,QAA2D,CAAC;CAClE,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,QAAQ,QAAQ,MAAM,KAAK;EACjC,IAAI,MAAM,WAAW,KAAK,MAAM,SAAS,GAAG,OAAO;EACnD,MAAM,QAAQ,eAAe,MAAM,EAAE;EACrC,IAAI,UAAU,MAAM,OAAO;EAE3B,IAAI;EACJ,IAAI,MAAM,WAAW,GAAG;GACtB,MAAM,WAAW,uBAAuB,KAAK,MAAM,EAAE;GACrD,IAAI,CAAC,UAAU,OAAO;GACtB,WAAW,OAAO,SAAS,EAAE;EAC/B;EACA,MAAM,KAAK;GAAE;GAAO;EAAS,CAAC;CAChC;CAEA,OAAO;EAAE;EAAO,OAAO,qBAAqB,KAAK;CAAE;AACrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"measureImage.js","names":[],"sources":["../../src/shared/measureImage.ts"],"sourcesContent":["import * as fs from \"fs\";\nimport { imageSize } from \"image-size\";\nimport type { DiagnosticCollector } from \"../diagnostics.ts\";\n\ntype ImageSizeCache = Map<string, { widthPx: number; heightPx: number }>;\ntype ImageDataCache = Map<string, string>;\n\n/**\n * キャッシュされた画像データ(Base64)を取得する\n * @param src 画像のパス\n * @param cache 画像データキャッシュ\n * @returns Base64形式の画像データ、またはキャッシュがない場合はundefined\n */\nexport function getImageData(\n src: string,\n cache: ImageDataCache,\n): string | undefined {\n return cache.get(src);\n}\n\n/**\n * 画像サイズを事前取得してキャッシュする(非同期)\n * HTTPS URLの画像を処理する際に使用\n * @param src 画像のパス(ローカルパス、base64データ、またはHTTPS URL)\n * @param sizeCache 画像サイズキャッシュ\n * @param dataCache 画像データキャッシュ\n * @returns 画像の幅と高さ(px)\n */\nexport async function prefetchImageSize(\n src: string,\n sizeCache: ImageSizeCache,\n dataCache: ImageDataCache,\n diagnostics: DiagnosticCollector,\n): Promise<{\n widthPx: number;\n heightPx: number;\n}> {\n // キャッシュにあればそれを返す\n const cached = sizeCache.get(src);\n if (cached) {\n return cached;\n }\n\n try {\n let buffer: Uint8Array;\n\n // base64データの場合\n if (src.startsWith(\"data:\")) {\n const base64Data = src.split(\",\")[1];\n buffer = new Uint8Array(Buffer.from(base64Data, \"base64\"));\n }\n // HTTPS/HTTP URLの場合\n else if (src.startsWith(\"https://\") || src.startsWith(\"http://\")) {\n const response = await fetch(src);\n if (!response.ok) {\n throw new Error(`Failed to fetch image: ${response.status}`);\n }\n const arrayBuffer = await response.arrayBuffer();\n buffer = new Uint8Array(arrayBuffer);\n\n // 画像データをBase64形式でキャッシュ(pptxgenjs用)\n const contentType = response.headers.get(\"content-type\") || \"image/png\";\n const base64 = Buffer.from(arrayBuffer).toString(\"base64\");\n dataCache.set(src, `${contentType};base64,${base64}`);\n }\n // ローカルファイルパスの場合\n else {\n buffer = new Uint8Array(fs.readFileSync(src));\n }\n\n const dimensions = imageSize(buffer);\n\n const width = dimensions.width ?? 100; // デフォルト100px\n const height = dimensions.height ?? 100; // デフォルト100px\n\n const result = {\n widthPx: width,\n heightPx: height,\n };\n\n // キャッシュに保存\n sizeCache.set(src, result);\n\n return result;\n } catch (error) {\n // エラーが発生した場合はデフォルトサイズを返す\n diagnostics.add(\n \"IMAGE_MEASURE_FAILED\",\n `Failed to measure image size for ${src}: ${String(error)}`,\n );\n const result = {\n widthPx: 100,\n heightPx: 100,\n };\n sizeCache.set(src, result);\n return result;\n }\n}\n\n/**\n * 画像ファイルのサイズを取得する(同期)\n * 事前にprefetchImageSizeでキャッシュしておくこと\n * @param src 画像のパス(ローカルパス、base64データ、またはHTTPS URL)\n * @param sizeCache 画像サイズキャッシュ\n * @returns 画像の幅と高さ(px)\n */\nexport function measureImage(\n src: string,\n sizeCache: ImageSizeCache,\n diagnostics: DiagnosticCollector,\n): {\n widthPx: number;\n heightPx: number;\n} {\n // キャッシュにあればそれを返す\n const cached = sizeCache.get(src);\n if (cached) {\n return cached;\n }\n\n // キャッシュにない場合(ローカルファイルやbase64のみ同期処理可能)\n try {\n let buffer: Uint8Array;\n\n // base64データの場合\n if (src.startsWith(\"data:\")) {\n const base64Data = src.split(\",\")[1];\n buffer = new Uint8Array(Buffer.from(base64Data, \"base64\"));\n }\n // HTTPS/HTTP URLの場合はキャッシュがないとデフォルト値を返す\n else if (src.startsWith(\"https://\") || src.startsWith(\"http://\")) {\n diagnostics.add(\n \"IMAGE_NOT_PREFETCHED\",\n `Image size for URL ${src} was not prefetched. Using default size.`,\n );\n return {\n widthPx: 100,\n heightPx: 100,\n };\n }\n // ローカルファイルパスの場合\n else {\n buffer = new Uint8Array(fs.readFileSync(src));\n }\n\n const dimensions = imageSize(buffer);\n\n const width = dimensions.width ?? 100; // デフォルト100px\n const height = dimensions.height ?? 100; // デフォルト100px\n\n const result = {\n widthPx: width,\n heightPx: height,\n };\n\n // キャッシュに保存\n sizeCache.set(src, result);\n\n return result;\n } catch (error) {\n // エラーが発生した場合はデフォルトサイズを返す\n diagnostics.add(\n \"IMAGE_MEASURE_FAILED\",\n `Failed to measure image size for ${src}: ${String(error)}`,\n );\n return {\n widthPx: 100,\n heightPx: 100,\n };\n }\n}\n"],"mappings":";;;;;;;;;AAaA,SAAgB,aACd,KACA,OACoB;CACpB,OAAO,MAAM,IAAI,GAAG;AACtB;;;;;;;;;AAUA,eAAsB,kBACpB,KACA,WACA,WACA,aAIC;CAED,MAAM,SAAS,UAAU,IAAI,GAAG;CAChC,IAAI,QACF,OAAO;CAGT,IAAI;EACF,IAAI;EAGJ,IAAI,IAAI,WAAW,OAAO,GAAG;GAC3B,MAAM,aAAa,IAAI,MAAM,GAAG,EAAE;GAClC,SAAS,IAAI,WAAW,OAAO,KAAK,YAAY,QAAQ,CAAC;EAC3D,OAEK,IAAI,IAAI,WAAW,UAAU,KAAK,IAAI,WAAW,SAAS,GAAG;GAChE,MAAM,WAAW,MAAM,MAAM,GAAG;GAChC,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,0BAA0B,SAAS,QAAQ;GAE7D,MAAM,cAAc,MAAM,SAAS,YAAY;GAC/C,SAAS,IAAI,WAAW,WAAW;GAGnC,MAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;GAC5D,MAAM,SAAS,OAAO,KAAK,WAAW,EAAE,SAAS,QAAQ;GACzD,UAAU,IAAI,KAAK,GAAG,YAAY,UAAU,QAAQ;EACtD,OAGE,SAAS,IAAI,WAAW,GAAG,aAAa,GAAG,CAAC;EAG9C,MAAM,aAAa,UAAU,MAAM;EAKnC,MAAM,SAAS;GACb,SAJY,WAAW,SAAS;GAKhC,UAJa,WAAW,UAAU;EAKpC;EAGA,UAAU,IAAI,KAAK,MAAM;EAEzB,OAAO;CACT,SAAS,OAAO;EAEd,YAAY,IACV,wBACA,oCAAoC,IAAI,IAAI,OAAO,KAAK,GAC1D;EACA,MAAM,SAAS;GACb,SAAS;GACT,UAAU;EACZ;EACA,UAAU,IAAI,KAAK,MAAM;EACzB,OAAO;CACT;AACF;;;;;;;;AASA,SAAgB,aACd,KACA,WACA,aAIA;CAEA,MAAM,SAAS,UAAU,IAAI,GAAG;CAChC,IAAI,QACF,OAAO;CAIT,IAAI;EACF,IAAI;EAGJ,IAAI,IAAI,WAAW,OAAO,GAAG;GAC3B,MAAM,aAAa,IAAI,MAAM,GAAG,EAAE;GAClC,SAAS,IAAI,WAAW,OAAO,KAAK,YAAY,QAAQ,CAAC;EAC3D,OAEK,IAAI,IAAI,WAAW,UAAU,KAAK,IAAI,WAAW,SAAS,GAAG;GAChE,YAAY,IACV,wBACA,sBAAsB,IAAI,yCAC5B;GACA,OAAO;IACL,SAAS;IACT,UAAU;GACZ;EACF,OAGE,SAAS,IAAI,WAAW,GAAG,aAAa,GAAG,CAAC;EAG9C,MAAM,aAAa,UAAU,MAAM;EAKnC,MAAM,SAAS;GACb,SAJY,WAAW,SAAS;GAKhC,UAJa,WAAW,UAAU;EAKpC;EAGA,UAAU,IAAI,KAAK,MAAM;EAEzB,OAAO;CACT,SAAS,OAAO;EAEd,YAAY,IACV,wBACA,oCAAoC,IAAI,IAAI,OAAO,KAAK,GAC1D;EACA,OAAO;GACL,SAAS;GACT,UAAU;EACZ;CACF;AACF"}
1
+ {"version":3,"file":"measureImage.js","names":[],"sources":["../../src/shared/measureImage.ts"],"sourcesContent":["import * as fs from \"fs\";\nimport { imageSize } from \"image-size\";\nimport type { DiagnosticCollector } from \"../diagnostics.ts\";\n\ntype ImageSizeCache = Map<string, { widthPx: number; heightPx: number }>;\ntype ImageDataCache = Map<string, string>;\n\n/**\n * キャッシュされた画像データ(Base64)を取得する\n * @param src 画像のパス\n * @param cache 画像データキャッシュ\n * @returns Base64形式の画像データ、またはキャッシュがない場合はundefined\n */\nexport function getImageData(\n src: string,\n cache: ImageDataCache,\n): string | undefined {\n return cache.get(src);\n}\n\n/**\n * 画像サイズを事前取得してキャッシュする(非同期)\n * HTTPS URLの画像を処理する際に使用\n * @param src 画像のパス(ローカルパス、base64データ、またはHTTPS URL)\n * @param sizeCache 画像サイズキャッシュ\n * @param dataCache 画像データキャッシュ\n * @returns 画像の幅と高さ(px)\n */\nexport async function prefetchImageSize(\n src: string,\n sizeCache: ImageSizeCache,\n dataCache: ImageDataCache,\n diagnostics: DiagnosticCollector,\n): Promise<{\n widthPx: number;\n heightPx: number;\n}> {\n // キャッシュにあればそれを返す\n const cached = sizeCache.get(src);\n if (cached) {\n return cached;\n }\n\n try {\n let buffer: Uint8Array;\n\n // base64データの場合\n if (src.startsWith(\"data:\")) {\n const base64Data = src.split(\",\")[1];\n buffer = new Uint8Array(Buffer.from(base64Data, \"base64\"));\n }\n // HTTPS/HTTP URLの場合\n else if (src.startsWith(\"https://\") || src.startsWith(\"http://\")) {\n const response = await fetch(src);\n if (!response.ok) {\n throw new Error(`Failed to fetch image: ${response.status}`);\n }\n const arrayBuffer = await response.arrayBuffer();\n buffer = new Uint8Array(arrayBuffer);\n\n // 画像データをBase64形式でキャッシュ(pptxgenjs用)\n const contentType = response.headers.get(\"content-type\") || \"image/png\";\n const base64 = Buffer.from(arrayBuffer).toString(\"base64\");\n dataCache.set(src, `${contentType};base64,${base64}`);\n }\n // ローカルファイルパスの場合\n else {\n buffer = new Uint8Array(fs.readFileSync(src));\n }\n\n const dimensions = imageSize(buffer);\n\n const width = dimensions.width ?? 100; // デフォルト100px\n const height = dimensions.height ?? 100; // デフォルト100px\n\n const result = {\n widthPx: width,\n heightPx: height,\n };\n\n // キャッシュに保存\n sizeCache.set(src, result);\n\n return result;\n } catch (error) {\n // エラーが発生した場合はデフォルトサイズを返す\n diagnostics.add(\n \"IMAGE_MEASURE_FAILED\",\n `Failed to measure image size for ${src}: ${String(error)}`,\n );\n const result = {\n widthPx: 100,\n heightPx: 100,\n };\n sizeCache.set(src, result);\n return result;\n }\n}\n\n/**\n * 画像ファイルのサイズを取得する(同期)\n * 事前にprefetchImageSizeでキャッシュしておくこと\n * @param src 画像のパス(ローカルパス、base64データ、またはHTTPS URL)\n * @param sizeCache 画像サイズキャッシュ\n * @returns 画像の幅と高さ(px)\n */\nexport function measureImage(\n src: string,\n sizeCache: ImageSizeCache,\n diagnostics: DiagnosticCollector,\n): {\n widthPx: number;\n heightPx: number;\n} {\n // キャッシュにあればそれを返す\n const cached = sizeCache.get(src);\n if (cached) {\n return cached;\n }\n\n // キャッシュにない場合(ローカルファイルやbase64のみ同期処理可能)\n try {\n let buffer: Uint8Array;\n\n // base64データの場合\n if (src.startsWith(\"data:\")) {\n const base64Data = src.split(\",\")[1];\n buffer = new Uint8Array(Buffer.from(base64Data, \"base64\"));\n }\n // HTTPS/HTTP URLの場合はキャッシュがないとデフォルト値を返す\n else if (src.startsWith(\"https://\") || src.startsWith(\"http://\")) {\n diagnostics.add(\n \"IMAGE_NOT_PREFETCHED\",\n `Image size for URL ${src} was not prefetched. Using default size.`,\n );\n return {\n widthPx: 100,\n heightPx: 100,\n };\n }\n // ローカルファイルパスの場合\n else {\n buffer = new Uint8Array(fs.readFileSync(src));\n }\n\n const dimensions = imageSize(buffer);\n\n const width = dimensions.width ?? 100; // デフォルト100px\n const height = dimensions.height ?? 100; // デフォルト100px\n\n const result = {\n widthPx: width,\n heightPx: height,\n };\n\n // キャッシュに保存\n sizeCache.set(src, result);\n\n return result;\n } catch (error) {\n // エラーが発生した場合はデフォルトサイズを返す\n diagnostics.add(\n \"IMAGE_MEASURE_FAILED\",\n `Failed to measure image size for ${src}: ${String(error)}`,\n );\n return {\n widthPx: 100,\n heightPx: 100,\n };\n }\n}\n"],"mappings":";;;;;;;;;AAaA,SAAgB,aACd,KACA,OACoB;CACpB,OAAO,MAAM,IAAI,GAAG;AACtB;;;;;;;;;AAUA,eAAsB,kBACpB,KACA,WACA,WACA,aAIC;CAED,MAAM,SAAS,UAAU,IAAI,GAAG;CAChC,IAAI,QACF,OAAO;CAGT,IAAI;EACF,IAAI;EAGJ,IAAI,IAAI,WAAW,OAAO,GAAG;GAC3B,MAAM,aAAa,IAAI,MAAM,GAAG,CAAC,CAAC;GAClC,SAAS,IAAI,WAAW,OAAO,KAAK,YAAY,QAAQ,CAAC;EAC3D,OAEK,IAAI,IAAI,WAAW,UAAU,KAAK,IAAI,WAAW,SAAS,GAAG;GAChE,MAAM,WAAW,MAAM,MAAM,GAAG;GAChC,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,0BAA0B,SAAS,QAAQ;GAE7D,MAAM,cAAc,MAAM,SAAS,YAAY;GAC/C,SAAS,IAAI,WAAW,WAAW;GAGnC,MAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;GAC5D,MAAM,SAAS,OAAO,KAAK,WAAW,CAAC,CAAC,SAAS,QAAQ;GACzD,UAAU,IAAI,KAAK,GAAG,YAAY,UAAU,QAAQ;EACtD,OAGE,SAAS,IAAI,WAAW,GAAG,aAAa,GAAG,CAAC;EAG9C,MAAM,aAAa,UAAU,MAAM;EAKnC,MAAM,SAAS;GACb,SAJY,WAAW,SAAS;GAKhC,UAJa,WAAW,UAAU;EAKpC;EAGA,UAAU,IAAI,KAAK,MAAM;EAEzB,OAAO;CACT,SAAS,OAAO;EAEd,YAAY,IACV,wBACA,oCAAoC,IAAI,IAAI,OAAO,KAAK,GAC1D;EACA,MAAM,SAAS;GACb,SAAS;GACT,UAAU;EACZ;EACA,UAAU,IAAI,KAAK,MAAM;EACzB,OAAO;CACT;AACF;;;;;;;;AASA,SAAgB,aACd,KACA,WACA,aAIA;CAEA,MAAM,SAAS,UAAU,IAAI,GAAG;CAChC,IAAI,QACF,OAAO;CAIT,IAAI;EACF,IAAI;EAGJ,IAAI,IAAI,WAAW,OAAO,GAAG;GAC3B,MAAM,aAAa,IAAI,MAAM,GAAG,CAAC,CAAC;GAClC,SAAS,IAAI,WAAW,OAAO,KAAK,YAAY,QAAQ,CAAC;EAC3D,OAEK,IAAI,IAAI,WAAW,UAAU,KAAK,IAAI,WAAW,SAAS,GAAG;GAChE,YAAY,IACV,wBACA,sBAAsB,IAAI,yCAC5B;GACA,OAAO;IACL,SAAS;IACT,UAAU;GACZ;EACF,OAGE,SAAS,IAAI,WAAW,GAAG,aAAa,GAAG,CAAC;EAG9C,MAAM,aAAa,UAAU,MAAM;EAKnC,MAAM,SAAS;GACb,SAJY,WAAW,SAAS;GAKhC,UAJa,WAAW,UAAU;EAKpC;EAGA,UAAU,IAAI,KAAK,MAAM;EAEzB,OAAO;CACT,SAAS,OAAO;EAEd,YAAY,IACV,wBACA,oCAAoC,IAAI,IAAI,OAAO,KAAK,GAC1D;EACA,OAAO;GACL,SAAS;GACT,UAAU;EACZ;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"tableUtils.js","names":[],"sources":["../../src/shared/tableUtils.ts"],"sourcesContent":["import type { TableNode } from \"../types.ts\";\n\nconst DEFAULT_TABLE_ROW_HEIGHT = 32;\nconst DEFAULT_TABLE_COLUMN_WIDTH = 100;\n\nexport function calcTableIntrinsicSize(node: TableNode) {\n const width = node.columns.reduce(\n (sum, column) => sum + (column.width ?? DEFAULT_TABLE_COLUMN_WIDTH),\n 0,\n );\n const height = resolveRowHeights(node).reduce((sum, h) => sum + h, 0);\n\n return { width, height };\n}\n\nexport function resolveRowHeights(node: TableNode) {\n const fallbackRowHeight = node.defaultRowHeight ?? DEFAULT_TABLE_ROW_HEIGHT;\n return node.rows.map((row) => row.height ?? fallbackRowHeight);\n}\n\n/**\n * テーブルの各カラム幅を解決する\n * - 幅が指定されているカラムはその値を使用\n * - 幅が未指定のカラムは、残りの幅を均等分割\n *\n * @param node テーブルノード\n * @param tableWidth テーブル全体の幅(レイアウト計算後の確定値)\n */\nexport function resolveColumnWidths(\n node: TableNode,\n tableWidth: number,\n): number[] {\n const specifiedTotal = node.columns.reduce(\n (sum, col) => sum + (col.width ?? 0),\n 0,\n );\n const unspecifiedCount = node.columns.filter(\n (col) => col.width === undefined,\n ).length;\n\n // 未指定カラムがない場合、または未指定カラムに割り当てる幅を計算\n const remainingWidth = Math.max(0, tableWidth - specifiedTotal);\n const widthPerUnspecified =\n unspecifiedCount > 0 ? remainingWidth / unspecifiedCount : 0;\n\n return node.columns.map((col) => col.width ?? widthPerUnspecified);\n}\n"],"mappings":";AAEA,MAAM,2BAA2B;AACjC,MAAM,6BAA6B;AAEnC,SAAgB,uBAAuB,MAAiB;CAOtD,OAAO;EAAE,OANK,KAAK,QAAQ,QACxB,KAAK,WAAW,OAAO,OAAO,SAAS,6BACxC,CAIW;EAAG,QAFD,kBAAkB,IAAI,EAAE,QAAQ,KAAK,MAAM,MAAM,GAAG,CAE9C;CAAE;AACzB;AAEA,SAAgB,kBAAkB,MAAiB;CACjD,MAAM,oBAAoB,KAAK,oBAAoB;CACnD,OAAO,KAAK,KAAK,KAAK,QAAQ,IAAI,UAAU,iBAAiB;AAC/D;;;;;;;;;AAUA,SAAgB,oBACd,MACA,YACU;CACV,MAAM,iBAAiB,KAAK,QAAQ,QACjC,KAAK,QAAQ,OAAO,IAAI,SAAS,IAClC,CACF;CACA,MAAM,mBAAmB,KAAK,QAAQ,QACnC,QAAQ,IAAI,UAAU,KAAA,CACzB,EAAE;CAGF,MAAM,iBAAiB,KAAK,IAAI,GAAG,aAAa,cAAc;CAC9D,MAAM,sBACJ,mBAAmB,IAAI,iBAAiB,mBAAmB;CAE7D,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI,SAAS,mBAAmB;AACnE"}
1
+ {"version":3,"file":"tableUtils.js","names":[],"sources":["../../src/shared/tableUtils.ts"],"sourcesContent":["import type { TableNode } from \"../types.ts\";\n\nconst DEFAULT_TABLE_ROW_HEIGHT = 32;\nconst DEFAULT_TABLE_COLUMN_WIDTH = 100;\n\nexport function calcTableIntrinsicSize(node: TableNode) {\n const width = node.columns.reduce(\n (sum, column) => sum + (column.width ?? DEFAULT_TABLE_COLUMN_WIDTH),\n 0,\n );\n const height = resolveRowHeights(node).reduce((sum, h) => sum + h, 0);\n\n return { width, height };\n}\n\nexport function resolveRowHeights(node: TableNode) {\n const fallbackRowHeight = node.defaultRowHeight ?? DEFAULT_TABLE_ROW_HEIGHT;\n return node.rows.map((row) => row.height ?? fallbackRowHeight);\n}\n\n/**\n * テーブルの各カラム幅を解決する\n * - 幅が指定されているカラムはその値を使用\n * - 幅が未指定のカラムは、残りの幅を均等分割\n *\n * @param node テーブルノード\n * @param tableWidth テーブル全体の幅(レイアウト計算後の確定値)\n */\nexport function resolveColumnWidths(\n node: TableNode,\n tableWidth: number,\n): number[] {\n const specifiedTotal = node.columns.reduce(\n (sum, col) => sum + (col.width ?? 0),\n 0,\n );\n const unspecifiedCount = node.columns.filter(\n (col) => col.width === undefined,\n ).length;\n\n // 未指定カラムがない場合、または未指定カラムに割り当てる幅を計算\n const remainingWidth = Math.max(0, tableWidth - specifiedTotal);\n const widthPerUnspecified =\n unspecifiedCount > 0 ? remainingWidth / unspecifiedCount : 0;\n\n return node.columns.map((col) => col.width ?? widthPerUnspecified);\n}\n"],"mappings":";AAEA,MAAM,2BAA2B;AACjC,MAAM,6BAA6B;AAEnC,SAAgB,uBAAuB,MAAiB;CAOtD,OAAO;EAAE,OANK,KAAK,QAAQ,QACxB,KAAK,WAAW,OAAO,OAAO,SAAS,6BACxC,CAIW;EAAG,QAFD,kBAAkB,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM,MAAM,GAAG,CAE9C;CAAE;AACzB;AAEA,SAAgB,kBAAkB,MAAiB;CACjD,MAAM,oBAAoB,KAAK,oBAAoB;CACnD,OAAO,KAAK,KAAK,KAAK,QAAQ,IAAI,UAAU,iBAAiB;AAC/D;;;;;;;;;AAUA,SAAgB,oBACd,MACA,YACU;CACV,MAAM,iBAAiB,KAAK,QAAQ,QACjC,KAAK,QAAQ,OAAO,IAAI,SAAS,IAClC,CACF;CACA,MAAM,mBAAmB,KAAK,QAAQ,QACnC,QAAQ,IAAI,UAAU,KAAA,CACzB,CAAC,CAAC;CAGF,MAAM,iBAAiB,KAAK,IAAI,GAAG,aAAa,cAAc;CAC9D,MAAM,sBACJ,mBAAmB,IAAI,iBAAiB,mBAAmB;CAE7D,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI,SAAS,mBAAmB;AACnE"}
@@ -4,13 +4,7 @@
4
4
  */
5
5
  function walkPOMTree(node, visitor) {
6
6
  visitor(node);
7
- switch (node.type) {
8
- case "vstack":
9
- case "hstack":
10
- case "layer":
11
- for (const child of node.children) walkPOMTree(child, visitor);
12
- break;
13
- }
7
+ if (node.type === "vstack" || node.type === "hstack" || node.type === "layer") for (const child of node.children) walkPOMTree(child, visitor);
14
8
  }
15
9
  //#endregion
16
10
  export { walkPOMTree };
@@ -1 +1 @@
1
- {"version":3,"file":"walkTree.js","names":[],"sources":["../../src/shared/walkTree.ts"],"sourcesContent":["import type { POMNode } from \"../types.ts\";\n\n/**\n * POMNode ツリーを再帰的に走査し、各ノードに visitor を適用する\n */\nexport function walkPOMTree(\n node: POMNode,\n visitor: (node: POMNode) => void,\n): void {\n visitor(node);\n\n switch (node.type) {\n case \"vstack\":\n case \"hstack\":\n case \"layer\":\n for (const child of node.children) {\n walkPOMTree(child, visitor);\n }\n break;\n }\n}\n"],"mappings":";;;;AAKA,SAAgB,YACd,MACA,SACM;CACN,QAAQ,IAAI;CAEZ,QAAQ,KAAK,MAAb;EACE,KAAK;EACL,KAAK;EACL,KAAK;GACH,KAAK,MAAM,SAAS,KAAK,UACvB,YAAY,OAAO,OAAO;GAE5B;CACJ;AACF"}
1
+ {"version":3,"file":"walkTree.js","names":[],"sources":["../../src/shared/walkTree.ts"],"sourcesContent":["import type { POMNode } from \"../types.ts\";\n\n/**\n * POMNode ツリーを再帰的に走査し、各ノードに visitor を適用する\n */\nexport function walkPOMTree(\n node: POMNode,\n visitor: (node: POMNode) => void,\n): void {\n visitor(node);\n\n if (\n node.type === \"vstack\" ||\n node.type === \"hstack\" ||\n node.type === \"layer\"\n ) {\n for (const child of node.children) {\n walkPOMTree(child, visitor);\n }\n }\n}\n"],"mappings":";;;;AAKA,SAAgB,YACd,MACA,SACM;CACN,QAAQ,IAAI;CAEZ,IACE,KAAK,SAAS,YACd,KAAK,SAAS,YACd,KAAK,SAAS,SAEd,KAAK,MAAM,SAAS,KAAK,UACvB,YAAY,OAAO,OAAO;AAGhC"}
@@ -16,7 +16,7 @@ async function toPositioned(pom, ctx, map, parentX = 0, parentY = 0) {
16
16
  const absoluteX = parentX + layout.left;
17
17
  const absoluteY = parentY + layout.top;
18
18
  const def = getNodeDef(pom.type);
19
- if (def.toPositioned) return def.toPositioned(pom, absoluteX, absoluteY, layout, ctx, map);
19
+ if (def.toPositioned) return def.toPositioned(pom, absoluteX, absoluteY, layout, ctx, map, toPositioned);
20
20
  switch (def.category) {
21
21
  case "leaf": return {
22
22
  ...pom,
@@ -1 +1 @@
1
- {"version":3,"file":"toPositioned.js","names":[],"sources":["../../src/toPositioned/toPositioned.ts"],"sourcesContent":["import type { POMNode, PositionedNode } from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { LayoutResultMap } from \"../calcYogaLayout/types.ts\";\nimport { getNodeDef } from \"../registry/index.ts\";\n\n/**\n * POMNode ツリーを絶対座標付きの PositionedNode ツリーに変換する\n * @param pom 入力 POMNode\n * @param ctx BuildContext\n * @param map LayoutResultMap(POMNode → 計算済みレイアウト結果のマッピング)\n * @param parentX 親ノードの絶対X座標\n * @param parentY 親ノードの絶対Y座標\n * @returns PositionedNode ツリー\n */\nexport async function toPositioned(\n pom: POMNode,\n ctx: BuildContext,\n map: LayoutResultMap,\n parentX = 0,\n parentY = 0,\n): Promise<PositionedNode> {\n const layout = map.get(pom);\n if (!layout) {\n throw new Error(\"Layout result not found in map for POMNode\");\n }\n const absoluteX = parentX + layout.left;\n const absoluteY = parentY + layout.top;\n\n const def = getNodeDef(pom.type);\n\n // ノード固有のカスタム変換がある場合はそれを使用\n if (def.toPositioned) {\n return def.toPositioned(pom, absoluteX, absoluteY, layout, ctx, map);\n }\n\n // category ベースのデフォルト処理\n switch (def.category) {\n case \"leaf\":\n return {\n ...pom,\n x: absoluteX,\n y: absoluteY,\n w: layout.width,\n h: layout.height,\n } as PositionedNode;\n\n case \"multi-child\": {\n const containerNode = pom as Extract<\n POMNode,\n { type: \"vstack\" | \"hstack\" }\n >;\n return {\n ...containerNode,\n x: absoluteX,\n y: absoluteY,\n w: layout.width,\n h: layout.height,\n children: await Promise.all(\n containerNode.children.map((child) =>\n toPositioned(child, ctx, map, absoluteX, absoluteY),\n ),\n ),\n };\n }\n\n case \"absolute-child\":\n // absolute-child (layer) は必ずカスタム toPositioned を持つべき\n throw new Error(\n `Node type \"${pom.type}\" with category \"absolute-child\" must have a custom toPositioned`,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;AAcA,eAAsB,aACpB,KACA,KACA,KACA,UAAU,GACV,UAAU,GACe;CACzB,MAAM,SAAS,IAAI,IAAI,GAAG;CAC1B,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,4CAA4C;CAE9D,MAAM,YAAY,UAAU,OAAO;CACnC,MAAM,YAAY,UAAU,OAAO;CAEnC,MAAM,MAAM,WAAW,IAAI,IAAI;CAG/B,IAAI,IAAI,cACN,OAAO,IAAI,aAAa,KAAK,WAAW,WAAW,QAAQ,KAAK,GAAG;CAIrE,QAAQ,IAAI,UAAZ;EACE,KAAK,QACH,OAAO;GACL,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG,OAAO;GACV,GAAG,OAAO;EACZ;EAEF,KAAK,eAAe;GAClB,MAAM,gBAAgB;GAItB,OAAO;IACL,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG,OAAO;IACV,GAAG,OAAO;IACV,UAAU,MAAM,QAAQ,IACtB,cAAc,SAAS,KAAK,UAC1B,aAAa,OAAO,KAAK,KAAK,WAAW,SAAS,CACpD,CACF;GACF;EACF;EAEA,KAAK,kBAEH,MAAM,IAAI,MACR,cAAc,IAAI,KAAK,iEACzB;CACJ;AACF"}
1
+ {"version":3,"file":"toPositioned.js","names":[],"sources":["../../src/toPositioned/toPositioned.ts"],"sourcesContent":["import type { POMNode, PositionedNode } from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { LayoutResultMap } from \"../calcYogaLayout/types.ts\";\nimport { getNodeDef } from \"../registry/index.ts\";\n\n/**\n * POMNode ツリーを絶対座標付きの PositionedNode ツリーに変換する\n * @param pom 入力 POMNode\n * @param ctx BuildContext\n * @param map LayoutResultMap(POMNode → 計算済みレイアウト結果のマッピング)\n * @param parentX 親ノードの絶対X座標\n * @param parentY 親ノードの絶対Y座標\n * @returns PositionedNode ツリー\n */\nexport async function toPositioned(\n pom: POMNode,\n ctx: BuildContext,\n map: LayoutResultMap,\n parentX = 0,\n parentY = 0,\n): Promise<PositionedNode> {\n const layout = map.get(pom);\n if (!layout) {\n throw new Error(\"Layout result not found in map for POMNode\");\n }\n const absoluteX = parentX + layout.left;\n const absoluteY = parentY + layout.top;\n\n const def = getNodeDef(pom.type);\n\n // ノード固有のカスタム変換がある場合はそれを使用\n if (def.toPositioned) {\n return def.toPositioned(\n pom,\n absoluteX,\n absoluteY,\n layout,\n ctx,\n map,\n toPositioned,\n );\n }\n\n // category ベースのデフォルト処理\n switch (def.category) {\n case \"leaf\":\n return {\n ...pom,\n x: absoluteX,\n y: absoluteY,\n w: layout.width,\n h: layout.height,\n } as PositionedNode;\n\n case \"multi-child\": {\n const containerNode = pom as Extract<\n POMNode,\n { type: \"vstack\" | \"hstack\" }\n >;\n return {\n ...containerNode,\n x: absoluteX,\n y: absoluteY,\n w: layout.width,\n h: layout.height,\n children: await Promise.all(\n containerNode.children.map((child) =>\n toPositioned(child, ctx, map, absoluteX, absoluteY),\n ),\n ),\n };\n }\n\n case \"absolute-child\":\n // absolute-child (layer) は必ずカスタム toPositioned を持つべき\n throw new Error(\n `Node type \"${pom.type}\" with category \"absolute-child\" must have a custom toPositioned`,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;AAcA,eAAsB,aACpB,KACA,KACA,KACA,UAAU,GACV,UAAU,GACe;CACzB,MAAM,SAAS,IAAI,IAAI,GAAG;CAC1B,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,4CAA4C;CAE9D,MAAM,YAAY,UAAU,OAAO;CACnC,MAAM,YAAY,UAAU,OAAO;CAEnC,MAAM,MAAM,WAAW,IAAI,IAAI;CAG/B,IAAI,IAAI,cACN,OAAO,IAAI,aACT,KACA,WACA,WACA,QACA,KACA,KACA,YACF;CAIF,QAAQ,IAAI,UAAZ;EACE,KAAK,QACH,OAAO;GACL,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG,OAAO;GACV,GAAG,OAAO;EACZ;EAEF,KAAK,eAAe;GAClB,MAAM,gBAAgB;GAItB,OAAO;IACL,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG,OAAO;IACV,GAAG,OAAO;IACV,UAAU,MAAM,QAAQ,IACtB,cAAc,SAAS,KAAK,UAC1B,aAAa,OAAO,KAAK,KAAK,WAAW,SAAS,CACpD,CACF;GACF;EACF;EAEA,KAAK,kBAEH,MAAM,IAAI,MACR,cAAc,IAAI,KAAK,iEACzB;CACJ;AACF"}