@hirokisakabe/pom 9.1.0 → 9.1.2

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 (52) hide show
  1. package/dist/buildContext.js +2 -4
  2. package/dist/buildContext.js.map +1 -1
  3. package/dist/buildPptx.d.ts.map +1 -1
  4. package/dist/buildPptx.js +2 -6
  5. package/dist/buildPptx.js.map +1 -1
  6. package/dist/extractSlideMastersAsPptx.js +1 -1
  7. package/dist/renderPptx/glimpseTextBoxes.js +646 -0
  8. package/dist/renderPptx/glimpseTextBoxes.js.map +1 -0
  9. package/dist/renderPptx/nodes/flow.js +95 -90
  10. package/dist/renderPptx/nodes/flow.js.map +1 -1
  11. package/dist/renderPptx/nodes/icon.js +33 -24
  12. package/dist/renderPptx/nodes/icon.js.map +1 -1
  13. package/dist/renderPptx/nodes/image.js +5 -22
  14. package/dist/renderPptx/nodes/image.js.map +1 -1
  15. package/dist/renderPptx/nodes/list.js +96 -111
  16. package/dist/renderPptx/nodes/list.js.map +1 -1
  17. package/dist/renderPptx/nodes/matrix.js +92 -70
  18. package/dist/renderPptx/nodes/matrix.js.map +1 -1
  19. package/dist/renderPptx/nodes/processArrow.js +68 -35
  20. package/dist/renderPptx/nodes/processArrow.js.map +1 -1
  21. package/dist/renderPptx/nodes/pyramid.js +34 -16
  22. package/dist/renderPptx/nodes/pyramid.js.map +1 -1
  23. package/dist/renderPptx/nodes/shape.js +63 -37
  24. package/dist/renderPptx/nodes/shape.js.map +1 -1
  25. package/dist/renderPptx/nodes/svg.js +2 -5
  26. package/dist/renderPptx/nodes/svg.js.map +1 -1
  27. package/dist/renderPptx/nodes/text.js +16 -45
  28. package/dist/renderPptx/nodes/text.js.map +1 -1
  29. package/dist/renderPptx/nodes/timeline.js +108 -74
  30. package/dist/renderPptx/nodes/timeline.js.map +1 -1
  31. package/dist/renderPptx/nodes/tree.js +78 -79
  32. package/dist/renderPptx/nodes/tree.js.map +1 -1
  33. package/dist/renderPptx/renderPptx.js +4 -5
  34. package/dist/renderPptx/renderPptx.js.map +1 -1
  35. package/dist/renderPptx/utils/backgroundBorder.js +85 -49
  36. package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
  37. package/dist/renderPptx/utils/glimpsePicture.js +130 -0
  38. package/dist/renderPptx/utils/glimpsePicture.js.map +1 -0
  39. package/dist/renderPptx/utils/glimpseShape.js +90 -0
  40. package/dist/renderPptx/utils/glimpseShape.js.map +1 -0
  41. package/dist/renderPptx/utils/glimpseTextBox.js +126 -0
  42. package/dist/renderPptx/utils/glimpseTextBox.js.map +1 -0
  43. package/dist/renderPptx/utils/straightLine.js +29 -14
  44. package/dist/renderPptx/utils/straightLine.js.map +1 -1
  45. package/dist/renderPptx/utils/visualStyle.js +1 -24
  46. package/dist/renderPptx/utils/visualStyle.js.map +1 -1
  47. package/dist/types.js +1 -1
  48. package/package.json +4 -4
  49. package/dist/renderPptx/glowEffects.js +0 -152
  50. package/dist/renderPptx/glowEffects.js.map +0 -1
  51. package/dist/renderPptx/gradientFills.js +0 -167
  52. package/dist/renderPptx/gradientFills.js.map +0 -1
@@ -1,8 +1,10 @@
1
- import { pxToIn, pxToPt } from "../units.js";
2
1
  import { withContentBounds } from "../utils/contentArea.js";
3
- import { registerBackgroundGradient } from "../gradientFills.js";
4
- import { stripHash } from "../utils/visualStyle.js";
2
+ import { parseGradient } from "../../shared/gradient.js";
3
+ import { addGlimpseTextBox } from "../utils/glimpseTextBox.js";
4
+ import { addGlimpseShape, createShapeBoundsInput, noShapeOutline, solidShapeFill } from "../utils/glimpseShape.js";
5
+ import { addStraightLine } from "../utils/straightLine.js";
5
6
  import { measureTimeline } from "../../calcYogaLayout/measureCompositeNodes.js";
7
+ import { stripHash } from "../utils/visualStyle.js";
6
8
  import { resolveScaledContentArea } from "../utils/scaleToFit.js";
7
9
  //#region src/renderPptx/nodes/timeline.ts
8
10
  function renderTimelineNode(node, ctx) {
@@ -17,7 +19,7 @@ function renderTimelineNode(node, ctx) {
17
19
  title: stripHash(node.titleColor) ?? "1E293B",
18
20
  description: stripHash(node.descriptionColor) ?? "64748B"
19
21
  };
20
- const connectorLineColor = (node.connectorGradient ? registerBackgroundGradient(node.connectorGradient, node.opacity, ctx.buildContext.gradientFills) : void 0) ?? stripHash(node.connectorColor) ?? "E2E8F0";
22
+ const connectorLineColor = stripHash(node.connectorGradient ? parseGradient(node.connectorGradient)?.value.stops[0]?.color : void 0) ?? stripHash(node.connectorColor) ?? "E2E8F0";
21
23
  const fontFace = node.fontFamily ?? "Noto Sans JP";
22
24
  const useColorForDate = node.useColorForDate ?? false;
23
25
  const { content, scaleFactor } = resolveScaledContentArea(node, measureTimeline(node), ctx);
@@ -31,6 +33,8 @@ function renderTimelineNode(node, ctx) {
31
33
  scaleFactor,
32
34
  textColors,
33
35
  connectorLineColor,
36
+ connectorGradient: node.connectorGradient,
37
+ connectorGradientOpacity: node.opacity,
34
38
  fontFace,
35
39
  useColorForDate
36
40
  };
@@ -54,15 +58,16 @@ function renderHorizontalTimeline(node, ctx, items, options) {
54
58
  const inset = Math.min(labelW / 2, node.w / 2);
55
59
  const startX = node.x + inset;
56
60
  const lineLength = node.x + node.w - inset - startX;
57
- ctx.slide.addShape(ctx.pptx.ShapeType.line, {
58
- x: pxToIn(startX),
59
- y: pxToIn(lineY),
60
- w: pxToIn(lineLength),
61
- h: 0,
62
- line: {
63
- color: connectorLineColor,
64
- width: pxToPt(lineWidth)
65
- }
61
+ addStraightLine(ctx, {
62
+ x1: startX,
63
+ y1: lineY,
64
+ x2: startX + lineLength,
65
+ y2: lineY
66
+ }, {
67
+ color: connectorLineColor,
68
+ lineWidth,
69
+ lineGradient: options.connectorGradient,
70
+ lineGradientOpacity: options.connectorGradientOpacity
66
71
  });
67
72
  const dateLabelH = 24 * scaleFactor;
68
73
  const titleLabelH = 24 * scaleFactor;
@@ -76,43 +81,57 @@ function renderHorizontalTimeline(node, ctx, items, options) {
76
81
  const cy = lineY;
77
82
  const color = item.color ?? defaultColor;
78
83
  const dateColor = resolveItemDateColor(item, options);
79
- ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {
80
- x: pxToIn(cx - nodeRadius),
81
- y: pxToIn(cy - nodeRadius),
82
- w: pxToIn(nodeRadius * 2),
83
- h: pxToIn(nodeRadius * 2),
84
- fill: { color },
85
- line: { type: "none" }
86
- });
87
- ctx.slide.addText(item.date, {
88
- x: pxToIn(cx - labelW / 2),
89
- y: pxToIn(cy - nodeRadius - dateOffset),
90
- w: pxToIn(labelW),
91
- h: pxToIn(dateLabelH),
92
- fontSize: pxToPt(12 * scaleFactor),
84
+ addGlimpseShape(ctx, {
85
+ preset: "ellipse",
86
+ ...createShapeBoundsInput({
87
+ x: cx - nodeRadius,
88
+ y: cy - nodeRadius,
89
+ w: nodeRadius * 2,
90
+ h: nodeRadius * 2
91
+ }),
92
+ fill: solidShapeFill(color),
93
+ outline: noShapeOutline()
94
+ }, {
95
+ x: cx - nodeRadius,
96
+ y: cy - nodeRadius,
97
+ w: nodeRadius * 2,
98
+ h: nodeRadius * 2
99
+ }, { fillColor: color });
100
+ addGlimpseTextBox(ctx, {
101
+ x: cx - labelW / 2,
102
+ y: cy - nodeRadius - dateOffset,
103
+ w: labelW,
104
+ h: dateLabelH
105
+ }, {
106
+ text: item.date,
107
+ fontSize: 12 * scaleFactor,
93
108
  fontFace,
94
109
  color: dateColor,
95
110
  align: "center",
96
111
  valign: "bottom"
97
112
  });
98
- ctx.slide.addText(item.title, {
99
- x: pxToIn(cx - labelW / 2),
100
- y: pxToIn(cy + nodeRadius + titleGap),
101
- w: pxToIn(labelW),
102
- h: pxToIn(titleLabelH),
103
- fontSize: pxToPt(14 * scaleFactor),
113
+ addGlimpseTextBox(ctx, {
114
+ x: cx - labelW / 2,
115
+ y: cy + nodeRadius + titleGap,
116
+ w: labelW,
117
+ h: titleLabelH
118
+ }, {
119
+ text: item.title,
120
+ fontSize: 14 * scaleFactor,
104
121
  fontFace,
105
122
  color: textColors.title,
106
123
  bold: true,
107
124
  align: "center",
108
125
  valign: "top"
109
126
  });
110
- if (item.description) ctx.slide.addText(item.description, {
111
- x: pxToIn(cx - labelW / 2),
112
- y: pxToIn(cy + nodeRadius + descOffset),
113
- w: pxToIn(labelW),
114
- h: pxToIn(descLabelH),
115
- fontSize: pxToPt(11 * scaleFactor),
127
+ if (item.description) addGlimpseTextBox(ctx, {
128
+ x: cx - labelW / 2,
129
+ y: cy + nodeRadius + descOffset,
130
+ w: labelW,
131
+ h: descLabelH
132
+ }, {
133
+ text: item.description,
134
+ fontSize: 11 * scaleFactor,
116
135
  fontFace,
117
136
  color: textColors.description,
118
137
  align: "center",
@@ -126,15 +145,16 @@ function renderVerticalTimeline(node, ctx, items, options) {
126
145
  const lineX = node.x + 40 * scaleFactor;
127
146
  const startY = node.y + nodeRadius;
128
147
  const lineLength = node.y + node.h - nodeRadius - startY;
129
- ctx.slide.addShape(ctx.pptx.ShapeType.line, {
130
- x: pxToIn(lineX),
131
- y: pxToIn(startY),
132
- w: 0,
133
- h: pxToIn(lineLength),
134
- line: {
135
- color: connectorLineColor,
136
- width: pxToPt(lineWidth)
137
- }
148
+ addStraightLine(ctx, {
149
+ x1: lineX,
150
+ y1: startY,
151
+ x2: lineX,
152
+ y2: startY + lineLength
153
+ }, {
154
+ color: connectorLineColor,
155
+ lineWidth,
156
+ lineGradient: options.connectorGradient,
157
+ lineGradientOpacity: options.connectorGradientOpacity
138
158
  });
139
159
  const labelGap = 16 * scaleFactor;
140
160
  const dateLabelW = 100 * scaleFactor;
@@ -149,43 +169,57 @@ function renderVerticalTimeline(node, ctx, items, options) {
149
169
  const cy = startY + lineLength * progress;
150
170
  const color = item.color ?? defaultColor;
151
171
  const dateColor = resolveItemDateColor(item, options);
152
- ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {
153
- x: pxToIn(cx - nodeRadius),
154
- y: pxToIn(cy - nodeRadius),
155
- w: pxToIn(nodeRadius * 2),
156
- h: pxToIn(nodeRadius * 2),
157
- fill: { color },
158
- line: { type: "none" }
159
- });
160
- ctx.slide.addText(item.date, {
161
- x: pxToIn(cx + nodeRadius + labelGap),
162
- y: pxToIn(cy - nodeRadius - 4 * scaleFactor),
163
- w: pxToIn(dateLabelW),
164
- h: pxToIn(dateLabelH),
165
- fontSize: pxToPt(12 * scaleFactor),
172
+ addGlimpseShape(ctx, {
173
+ preset: "ellipse",
174
+ ...createShapeBoundsInput({
175
+ x: cx - nodeRadius,
176
+ y: cy - nodeRadius,
177
+ w: nodeRadius * 2,
178
+ h: nodeRadius * 2
179
+ }),
180
+ fill: solidShapeFill(color),
181
+ outline: noShapeOutline()
182
+ }, {
183
+ x: cx - nodeRadius,
184
+ y: cy - nodeRadius,
185
+ w: nodeRadius * 2,
186
+ h: nodeRadius * 2
187
+ }, { fillColor: color });
188
+ addGlimpseTextBox(ctx, {
189
+ x: cx + nodeRadius + labelGap,
190
+ y: cy - nodeRadius - 4 * scaleFactor,
191
+ w: dateLabelW,
192
+ h: dateLabelH
193
+ }, {
194
+ text: item.date,
195
+ fontSize: 12 * scaleFactor,
166
196
  fontFace,
167
197
  color: dateColor,
168
198
  align: "left",
169
199
  valign: "bottom"
170
200
  });
171
- ctx.slide.addText(item.title, {
172
- x: pxToIn(cx + nodeRadius + labelGap),
173
- y: pxToIn(cy - 4 * scaleFactor),
174
- w: pxToIn(titleLabelW),
175
- h: pxToIn(titleLabelH),
176
- fontSize: pxToPt(14 * scaleFactor),
201
+ addGlimpseTextBox(ctx, {
202
+ x: cx + nodeRadius + labelGap,
203
+ y: cy - 4 * scaleFactor,
204
+ w: titleLabelW,
205
+ h: titleLabelH
206
+ }, {
207
+ text: item.title,
208
+ fontSize: 14 * scaleFactor,
177
209
  fontFace,
178
210
  color: textColors.title,
179
211
  bold: true,
180
212
  align: "left",
181
213
  valign: "top"
182
214
  });
183
- if (item.description) ctx.slide.addText(item.description, {
184
- x: pxToIn(cx + nodeRadius + labelGap),
185
- y: pxToIn(cy + 20 * scaleFactor),
186
- w: pxToIn(descLabelW),
187
- h: pxToIn(descLabelH),
188
- fontSize: pxToPt(11 * scaleFactor),
215
+ if (item.description) addGlimpseTextBox(ctx, {
216
+ x: cx + nodeRadius + labelGap,
217
+ y: cy + 20 * scaleFactor,
218
+ w: descLabelW,
219
+ h: descLabelH
220
+ }, {
221
+ text: item.description,
222
+ fontSize: 11 * scaleFactor,
189
223
  fontFace,
190
224
  color: textColors.description,
191
225
  align: "left",
@@ -1 +1 @@
1
- {"version":3,"file":"timeline.js","names":[],"sources":["../../../src/renderPptx/nodes/timeline.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { measureTimeline } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport { withContentBounds } from \"../utils/contentArea.ts\";\nimport { registerBackgroundGradient } from \"../gradientFills.ts\";\n\ntype TimelinePositionedNode = Extract<PositionedNode, { type: \"timeline\" }>;\n\ntype TimelineTextColors = {\n date: string;\n title: string;\n description: string;\n};\n\ntype TimelineRenderOptions = {\n defaultColor: string;\n nodeRadius: number;\n lineWidth: number;\n scaleFactor: number;\n textColors: TimelineTextColors;\n connectorLineColor: string;\n fontFace: string;\n useColorForDate: boolean;\n};\n\nexport function renderTimelineNode(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"horizontal\";\n const items = node.items;\n const itemCount = items.length;\n\n if (itemCount === 0) return;\n\n const defaultColor = \"1D4ED8\"; // blue\n const baseNodeRadius = 12; // px\n const baseLineWidth = 4; // px\n\n const textColors: TimelineTextColors = {\n date: stripHash(node.dateColor) ?? \"64748B\",\n title: stripHash(node.titleColor) ?? \"1E293B\",\n description: stripHash(node.descriptionColor) ?? \"64748B\",\n };\n\n // 軸線色を解決する。connectorGradient が指定されていれば gradient registry に\n // 登録してマーカー色を採用する (後処理で gradFill に置換される)。\n // connectorColor が指定されていればそれを採用、未指定ならデフォルトの E2E8F0。\n const connectorGradientMarker = node.connectorGradient\n ? registerBackgroundGradient(\n node.connectorGradient,\n node.opacity,\n ctx.buildContext.gradientFills,\n )\n : undefined;\n const connectorLineColor =\n connectorGradientMarker ?? stripHash(node.connectorColor) ?? \"E2E8F0\";\n\n const fontFace = node.fontFamily ?? \"Noto Sans JP\";\n const useColorForDate = node.useColorForDate ?? false;\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measureTimeline(node),\n ctx,\n );\n\n const nodeRadius = baseNodeRadius * scaleFactor;\n const lineWidth = baseLineWidth * scaleFactor;\n\n // コンテンツ領域を使用するための仮想ノードを作成\n const contentNode = withContentBounds(node, content);\n\n const options: TimelineRenderOptions = {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n fontFace,\n useColorForDate,\n };\n\n if (direction === \"horizontal\") {\n renderHorizontalTimeline(contentNode, ctx, items, options);\n } else {\n renderVerticalTimeline(contentNode, ctx, items, options);\n }\n}\n\nfunction resolveItemDateColor(\n item: TimelinePositionedNode[\"items\"][number],\n options: TimelineRenderOptions,\n): string {\n // 優先順位: item.dateColor > (useColorForDate && item.color) > Timeline.dateColor\n const perItemDateColor = stripHash(item.dateColor);\n if (perItemDateColor) return perItemDateColor;\n if (options.useColorForDate && item.color) {\n const inherited = stripHash(item.color);\n if (inherited) return inherited;\n }\n return options.textColors.date;\n}\n\nfunction renderHorizontalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n options: TimelineRenderOptions,\n): void {\n const {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n fontFace,\n } = options;\n const itemCount = items.length;\n const lineY = node.y + node.h / 2;\n const labelW = 120 * scaleFactor;\n // 極端に狭い node.w でも startX <= endX を保つため、インセットを node.w/2 で頭打ちする\n const inset = Math.min(labelW / 2, node.w / 2);\n const startX = node.x + inset;\n const endX = node.x + node.w - inset;\n const lineLength = endX - startX;\n\n // メインの線を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(startX),\n y: pxToIn(lineY),\n w: pxToIn(lineLength),\n h: 0,\n line: { color: connectorLineColor, width: pxToPt(lineWidth) },\n });\n const dateLabelH = 24 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const dateOffset = 40 * scaleFactor;\n const titleGap = 8 * scaleFactor;\n const descOffset = 32 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = startX + lineLength * progress;\n const cy = lineY;\n const color = item.color ?? defaultColor;\n const dateColor = resolveItemDateColor(item, options);\n\n // ノード(円)を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {\n x: pxToIn(cx - nodeRadius),\n y: pxToIn(cy - nodeRadius),\n w: pxToIn(nodeRadius * 2),\n h: pxToIn(nodeRadius * 2),\n fill: { color },\n line: { type: \"none\" as const },\n });\n\n // 日付を上に表示\n ctx.slide.addText(item.date, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy - nodeRadius - dateOffset),\n w: pxToIn(labelW),\n h: pxToIn(dateLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace,\n color: dateColor,\n align: \"center\",\n valign: \"bottom\",\n });\n\n // タイトルを下に表示\n ctx.slide.addText(item.title, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy + nodeRadius + titleGap),\n w: pxToIn(labelW),\n h: pxToIn(titleLabelH),\n fontSize: pxToPt(14 * scaleFactor),\n fontFace,\n color: textColors.title,\n bold: true,\n align: \"center\",\n valign: \"top\",\n });\n\n // 説明を表示\n if (item.description) {\n ctx.slide.addText(item.description, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy + nodeRadius + descOffset),\n w: pxToIn(labelW),\n h: pxToIn(descLabelH),\n fontSize: pxToPt(11 * scaleFactor),\n fontFace,\n color: textColors.description,\n align: \"center\",\n valign: \"top\",\n });\n }\n });\n}\n\nfunction renderVerticalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n options: TimelineRenderOptions,\n): void {\n const {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n fontFace,\n } = options;\n const itemCount = items.length;\n const lineX = node.x + 40 * scaleFactor;\n const startY = node.y + nodeRadius;\n const endY = node.y + node.h - nodeRadius;\n const lineLength = endY - startY;\n\n // メインの線を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(lineX),\n y: pxToIn(startY),\n w: 0,\n h: pxToIn(lineLength),\n line: { color: connectorLineColor, width: pxToPt(lineWidth) },\n });\n\n const labelGap = 16 * scaleFactor;\n const dateLabelW = 100 * scaleFactor;\n const dateLabelH = 20 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const titleLabelW = node.w - 80 * scaleFactor;\n const descLabelW = node.w - 80 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = lineX;\n const cy = startY + lineLength * progress;\n const color = item.color ?? defaultColor;\n const dateColor = resolveItemDateColor(item, options);\n\n // ノード(円)を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {\n x: pxToIn(cx - nodeRadius),\n y: pxToIn(cy - nodeRadius),\n w: pxToIn(nodeRadius * 2),\n h: pxToIn(nodeRadius * 2),\n fill: { color },\n line: { type: \"none\" as const },\n });\n\n // 日付を左上に表示\n ctx.slide.addText(item.date, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy - nodeRadius - 4 * scaleFactor),\n w: pxToIn(dateLabelW),\n h: pxToIn(dateLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace,\n color: dateColor,\n align: \"left\",\n valign: \"bottom\",\n });\n\n // タイトルを右に表示\n ctx.slide.addText(item.title, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy - 4 * scaleFactor),\n w: pxToIn(titleLabelW),\n h: pxToIn(titleLabelH),\n fontSize: pxToPt(14 * scaleFactor),\n fontFace,\n color: textColors.title,\n bold: true,\n align: \"left\",\n valign: \"top\",\n });\n\n // 説明を表示\n if (item.description) {\n ctx.slide.addText(item.description, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy + 20 * scaleFactor),\n w: pxToIn(descLabelW),\n h: pxToIn(descLabelH),\n fontSize: pxToPt(11 * scaleFactor),\n fontFace,\n color: textColors.description,\n align: \"left\",\n valign: \"top\",\n });\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"timeline.js","names":[],"sources":["../../../src/renderPptx/nodes/timeline.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { measureTimeline } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport { withContentBounds } from \"../utils/contentArea.ts\";\nimport { parseGradient } from \"../../shared/gradient.ts\";\nimport { addStraightLine } from \"../utils/straightLine.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n noShapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { addGlimpseTextBox } from \"../utils/glimpseTextBox.ts\";\n\ntype TimelinePositionedNode = Extract<PositionedNode, { type: \"timeline\" }>;\n\ntype TimelineTextColors = {\n date: string;\n title: string;\n description: string;\n};\n\ntype TimelineRenderOptions = {\n defaultColor: string;\n nodeRadius: number;\n lineWidth: number;\n scaleFactor: number;\n textColors: TimelineTextColors;\n connectorLineColor: string;\n connectorGradient?: string;\n connectorGradientOpacity?: number;\n fontFace: string;\n useColorForDate: boolean;\n};\n\nexport function renderTimelineNode(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"horizontal\";\n const items = node.items;\n const itemCount = items.length;\n\n if (itemCount === 0) return;\n\n const defaultColor = \"1D4ED8\"; // blue\n const baseNodeRadius = 12; // px\n const baseLineWidth = 4; // px\n\n const textColors: TimelineTextColors = {\n date: stripHash(node.dateColor) ?? \"64748B\",\n title: stripHash(node.titleColor) ?? \"1E293B\",\n description: stripHash(node.descriptionColor) ?? \"64748B\",\n };\n\n const connectorGradientFallback = node.connectorGradient\n ? parseGradient(node.connectorGradient)?.value.stops[0]?.color\n : undefined;\n const connectorLineColor =\n stripHash(connectorGradientFallback) ??\n stripHash(node.connectorColor) ??\n \"E2E8F0\";\n\n const fontFace = node.fontFamily ?? \"Noto Sans JP\";\n const useColorForDate = node.useColorForDate ?? false;\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measureTimeline(node),\n ctx,\n );\n\n const nodeRadius = baseNodeRadius * scaleFactor;\n const lineWidth = baseLineWidth * scaleFactor;\n\n // コンテンツ領域を使用するための仮想ノードを作成\n const contentNode = withContentBounds(node, content);\n\n const options: TimelineRenderOptions = {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n connectorGradient: node.connectorGradient,\n connectorGradientOpacity: node.opacity,\n fontFace,\n useColorForDate,\n };\n\n if (direction === \"horizontal\") {\n renderHorizontalTimeline(contentNode, ctx, items, options);\n } else {\n renderVerticalTimeline(contentNode, ctx, items, options);\n }\n}\n\nfunction resolveItemDateColor(\n item: TimelinePositionedNode[\"items\"][number],\n options: TimelineRenderOptions,\n): string {\n // 優先順位: item.dateColor > (useColorForDate && item.color) > Timeline.dateColor\n const perItemDateColor = stripHash(item.dateColor);\n if (perItemDateColor) return perItemDateColor;\n if (options.useColorForDate && item.color) {\n const inherited = stripHash(item.color);\n if (inherited) return inherited;\n }\n return options.textColors.date;\n}\n\nfunction renderHorizontalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n options: TimelineRenderOptions,\n): void {\n const {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n fontFace,\n } = options;\n const itemCount = items.length;\n const lineY = node.y + node.h / 2;\n const labelW = 120 * scaleFactor;\n // 極端に狭い node.w でも startX <= endX を保つため、インセットを node.w/2 で頭打ちする\n const inset = Math.min(labelW / 2, node.w / 2);\n const startX = node.x + inset;\n const endX = node.x + node.w - inset;\n const lineLength = endX - startX;\n\n // メインの線を描画\n addStraightLine(\n ctx,\n { x1: startX, y1: lineY, x2: startX + lineLength, y2: lineY },\n {\n color: connectorLineColor,\n lineWidth,\n lineGradient: options.connectorGradient,\n lineGradientOpacity: options.connectorGradientOpacity,\n },\n );\n const dateLabelH = 24 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const dateOffset = 40 * scaleFactor;\n const titleGap = 8 * scaleFactor;\n const descOffset = 32 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = startX + lineLength * progress;\n const cy = lineY;\n const color = item.color ?? defaultColor;\n const dateColor = resolveItemDateColor(item, options);\n\n // ノード(円)を描画\n addGlimpseShape(\n ctx,\n {\n preset: \"ellipse\",\n ...createShapeBoundsInput({\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n }),\n fill: solidShapeFill(color),\n outline: noShapeOutline(),\n },\n {\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n },\n { fillColor: color },\n );\n\n // 日付を上に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx - labelW / 2,\n y: cy - nodeRadius - dateOffset,\n w: labelW,\n h: dateLabelH,\n },\n {\n text: item.date,\n fontSize: 12 * scaleFactor,\n fontFace,\n color: dateColor,\n align: \"center\",\n valign: \"bottom\",\n },\n );\n\n // タイトルを下に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx - labelW / 2,\n y: cy + nodeRadius + titleGap,\n w: labelW,\n h: titleLabelH,\n },\n {\n text: item.title,\n fontSize: 14 * scaleFactor,\n fontFace,\n color: textColors.title,\n bold: true,\n align: \"center\",\n valign: \"top\",\n },\n );\n\n // 説明を表示\n if (item.description) {\n addGlimpseTextBox(\n ctx,\n {\n x: cx - labelW / 2,\n y: cy + nodeRadius + descOffset,\n w: labelW,\n h: descLabelH,\n },\n {\n text: item.description,\n fontSize: 11 * scaleFactor,\n fontFace,\n color: textColors.description,\n align: \"center\",\n valign: \"top\",\n },\n );\n }\n });\n}\n\nfunction renderVerticalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n options: TimelineRenderOptions,\n): void {\n const {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n fontFace,\n } = options;\n const itemCount = items.length;\n const lineX = node.x + 40 * scaleFactor;\n const startY = node.y + nodeRadius;\n const endY = node.y + node.h - nodeRadius;\n const lineLength = endY - startY;\n\n // メインの線を描画\n addStraightLine(\n ctx,\n { x1: lineX, y1: startY, x2: lineX, y2: startY + lineLength },\n {\n color: connectorLineColor,\n lineWidth,\n lineGradient: options.connectorGradient,\n lineGradientOpacity: options.connectorGradientOpacity,\n },\n );\n\n const labelGap = 16 * scaleFactor;\n const dateLabelW = 100 * scaleFactor;\n const dateLabelH = 20 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const titleLabelW = node.w - 80 * scaleFactor;\n const descLabelW = node.w - 80 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = lineX;\n const cy = startY + lineLength * progress;\n const color = item.color ?? defaultColor;\n const dateColor = resolveItemDateColor(item, options);\n\n // ノード(円)を描画\n addGlimpseShape(\n ctx,\n {\n preset: \"ellipse\",\n ...createShapeBoundsInput({\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n }),\n fill: solidShapeFill(color),\n outline: noShapeOutline(),\n },\n {\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n },\n { fillColor: color },\n );\n\n // 日付を左上に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx + nodeRadius + labelGap,\n y: cy - nodeRadius - 4 * scaleFactor,\n w: dateLabelW,\n h: dateLabelH,\n },\n {\n text: item.date,\n fontSize: 12 * scaleFactor,\n fontFace,\n color: dateColor,\n align: \"left\",\n valign: \"bottom\",\n },\n );\n\n // タイトルを右に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx + nodeRadius + labelGap,\n y: cy - 4 * scaleFactor,\n w: titleLabelW,\n h: titleLabelH,\n },\n {\n text: item.title,\n fontSize: 14 * scaleFactor,\n fontFace,\n color: textColors.title,\n bold: true,\n align: \"left\",\n valign: \"top\",\n },\n );\n\n // 説明を表示\n if (item.description) {\n addGlimpseTextBox(\n ctx,\n {\n x: cx + nodeRadius + labelGap,\n y: cy + 20 * scaleFactor,\n w: descLabelW,\n h: descLabelH,\n },\n {\n text: item.description,\n fontSize: 11 * scaleFactor,\n fontFace,\n color: textColors.description,\n align: \"left\",\n valign: \"top\",\n },\n );\n }\n });\n}\n"],"mappings":"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"}
@@ -1,4 +1,6 @@
1
- import { pxToIn, pxToPt } from "../units.js";
1
+ import { createGlimpseParagraphs } from "../utils/glimpseTextBox.js";
2
+ import { addGlimpseShape, createShapeBoundsInput, shapeOutline, solidShapeFill } from "../utils/glimpseShape.js";
3
+ import { addStraightLine } from "../utils/straightLine.js";
2
4
  import { stripHash } from "../utils/visualStyle.js";
3
5
  import { resolveScaledContentArea } from "../utils/scaleToFit.js";
4
6
  //#region src/renderPptx/nodes/tree.ts
@@ -86,37 +88,34 @@ function renderTreeNode(node, ctx) {
86
88
  const childCenterX = ox + (child.x + child.width / 2) * sf;
87
89
  const childTopY = oy + child.y * sf;
88
90
  const midY = (parentBottomY + childTopY) / 2;
89
- ctx.slide.addShape(ctx.pptx.ShapeType.line, {
90
- x: pxToIn(parentCenterX),
91
- y: pxToIn(parentBottomY),
92
- w: 0,
93
- h: pxToIn(midY - parentBottomY),
94
- line: {
95
- color: lineColor,
96
- width: pxToPt(lineWidth * sf)
97
- }
91
+ addStraightLine(ctx, {
92
+ x1: parentCenterX,
93
+ y1: parentBottomY,
94
+ x2: parentCenterX,
95
+ y2: midY
96
+ }, {
97
+ color: lineColor,
98
+ lineWidth: lineWidth * sf
98
99
  });
99
100
  const minX = Math.min(parentCenterX, childCenterX);
100
101
  const maxX = Math.max(parentCenterX, childCenterX);
101
- if (maxX > minX) ctx.slide.addShape(ctx.pptx.ShapeType.line, {
102
- x: pxToIn(minX),
103
- y: pxToIn(midY),
104
- w: pxToIn(maxX - minX),
105
- h: 0,
106
- line: {
107
- color: lineColor,
108
- width: pxToPt(lineWidth * sf)
109
- }
102
+ if (maxX > minX) addStraightLine(ctx, {
103
+ x1: minX,
104
+ y1: midY,
105
+ x2: maxX,
106
+ y2: midY
107
+ }, {
108
+ color: lineColor,
109
+ lineWidth: lineWidth * sf
110
110
  });
111
- ctx.slide.addShape(ctx.pptx.ShapeType.line, {
112
- x: pxToIn(childCenterX),
113
- y: pxToIn(midY),
114
- w: 0,
115
- h: pxToIn(childTopY - midY),
116
- line: {
117
- color: lineColor,
118
- width: pxToPt(lineWidth * sf)
119
- }
111
+ addStraightLine(ctx, {
112
+ x1: childCenterX,
113
+ y1: midY,
114
+ x2: childCenterX,
115
+ y2: childTopY
116
+ }, {
117
+ color: lineColor,
118
+ lineWidth: lineWidth * sf
120
119
  });
121
120
  } else {
122
121
  const parentRightX = ox + (parent.x + parent.width) * sf;
@@ -124,37 +123,34 @@ function renderTreeNode(node, ctx) {
124
123
  const childLeftX = ox + child.x * sf;
125
124
  const childCenterY = oy + (child.y + child.height / 2) * sf;
126
125
  const midX = (parentRightX + childLeftX) / 2;
127
- ctx.slide.addShape(ctx.pptx.ShapeType.line, {
128
- x: pxToIn(parentRightX),
129
- y: pxToIn(parentCenterY),
130
- w: pxToIn(midX - parentRightX),
131
- h: 0,
132
- line: {
133
- color: lineColor,
134
- width: pxToPt(lineWidth * sf)
135
- }
126
+ addStraightLine(ctx, {
127
+ x1: parentRightX,
128
+ y1: parentCenterY,
129
+ x2: midX,
130
+ y2: parentCenterY
131
+ }, {
132
+ color: lineColor,
133
+ lineWidth: lineWidth * sf
136
134
  });
137
135
  const minY = Math.min(parentCenterY, childCenterY);
138
136
  const maxY = Math.max(parentCenterY, childCenterY);
139
- if (maxY > minY) ctx.slide.addShape(ctx.pptx.ShapeType.line, {
140
- x: pxToIn(midX),
141
- y: pxToIn(minY),
142
- w: 0,
143
- h: pxToIn(maxY - minY),
144
- line: {
145
- color: lineColor,
146
- width: pxToPt(lineWidth * sf)
147
- }
137
+ if (maxY > minY) addStraightLine(ctx, {
138
+ x1: midX,
139
+ y1: minY,
140
+ x2: midX,
141
+ y2: maxY
142
+ }, {
143
+ color: lineColor,
144
+ lineWidth: lineWidth * sf
148
145
  });
149
- ctx.slide.addShape(ctx.pptx.ShapeType.line, {
150
- x: pxToIn(midX),
151
- y: pxToIn(childCenterY),
152
- w: pxToIn(childLeftX - midX),
153
- h: 0,
154
- line: {
155
- color: lineColor,
156
- width: pxToPt(lineWidth * sf)
157
- }
146
+ addStraightLine(ctx, {
147
+ x1: midX,
148
+ y1: childCenterY,
149
+ x2: childLeftX,
150
+ y2: childCenterY
151
+ }, {
152
+ color: lineColor,
153
+ lineWidth: lineWidth * sf
158
154
  });
159
155
  }
160
156
  }
@@ -162,37 +158,40 @@ function renderTreeNode(node, ctx) {
162
158
  const color = layoutNode.item.color ?? defaultNodeColor;
163
159
  const shapeType = (() => {
164
160
  switch (shape) {
165
- case "rect": return ctx.pptx.ShapeType.rect;
166
- case "roundRect": return ctx.pptx.ShapeType.roundRect;
167
- case "ellipse": return ctx.pptx.ShapeType.ellipse;
161
+ case "rect": return "rect";
162
+ case "roundRect": return "roundRect";
163
+ case "ellipse": return "ellipse";
168
164
  }
169
165
  })();
170
166
  const drawX = ox + layoutNode.x * sf;
171
167
  const drawY = oy + layoutNode.y * sf;
172
168
  const drawW = layoutNode.width * sf;
173
169
  const drawH = layoutNode.height * sf;
174
- ctx.slide.addShape(shapeType, {
175
- x: pxToIn(drawX),
176
- y: pxToIn(drawY),
177
- w: pxToIn(drawW),
178
- h: pxToIn(drawH),
179
- fill: { color },
180
- line: {
170
+ addGlimpseShape(ctx, {
171
+ preset: shapeType,
172
+ ...createShapeBoundsInput({
173
+ x: drawX,
174
+ y: drawY,
175
+ w: drawW,
176
+ h: drawH
177
+ }),
178
+ fill: solidShapeFill(color),
179
+ outline: shapeOutline({
181
180
  color: "333333",
182
- width: pxToPt(1 * sf)
183
- }
184
- });
185
- ctx.slide.addText(layoutNode.item.label, {
186
- x: pxToIn(drawX),
187
- y: pxToIn(drawY),
188
- w: pxToIn(drawW),
189
- h: pxToIn(drawH),
190
- fontSize: pxToPt(12 * sf),
191
- fontFace: "Noto Sans JP",
192
- color: stripHash(layoutNode.item.textColor) ?? defaultTextColor,
193
- align: "center",
194
- valign: "middle"
195
- });
181
+ width: 1 * sf
182
+ }),
183
+ body: { anchor: "middle" },
184
+ paragraphs: createGlimpseParagraphs(layoutNode.item.label, {
185
+ fontSize: 12 * sf,
186
+ fontFace: "Noto Sans JP",
187
+ color: stripHash(layoutNode.item.textColor) ?? defaultTextColor
188
+ }, { align: "center" })
189
+ }, {
190
+ x: drawX,
191
+ y: drawY,
192
+ w: drawW,
193
+ h: drawH
194
+ }, { fillColor: color });
196
195
  }
197
196
  function drawAllConnectors(layoutNode, sf, ox, oy) {
198
197
  for (const child of layoutNode.children) {
@@ -1 +1 @@
1
- {"version":3,"file":"tree.js","names":[],"sources":["../../../src/renderPptx/nodes/tree.ts"],"sourcesContent":["import type {\n PositionedNode,\n TreeDataItem,\n TreeNodeShape,\n TreeConnectorStyle,\n} from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\n\ntype TreePositionedNode = Extract<PositionedNode, { type: \"tree\" }>;\n\ninterface LayoutNode {\n item: TreeDataItem;\n x: number;\n y: number;\n width: number;\n height: number;\n children: LayoutNode[];\n}\n\nexport function renderTreeNode(\n node: TreePositionedNode,\n ctx: RenderContext,\n): void {\n const layout = node.layout ?? \"vertical\";\n const nodeShape = node.nodeShape ?? \"rect\";\n const nodeWidth = node.nodeWidth ?? 120;\n const nodeHeight = node.nodeHeight ?? 40;\n const levelGap = node.levelGap ?? 60;\n const siblingGap = node.siblingGap ?? 20;\n const connectorStyle = node.connectorStyle ?? {};\n const defaultColor = \"1D4ED8\";\n const defaultTextColor = stripHash(node.textColor) ?? \"FFFFFF\";\n\n // サブツリーの幅/高さを計算\n function calculateSubtreeSize(item: TreeDataItem): {\n width: number;\n height: number;\n } {\n if (!item.children || item.children.length === 0) {\n return { width: nodeWidth, height: nodeHeight };\n }\n\n const childSizes = item.children.map(calculateSubtreeSize);\n\n if (layout === \"vertical\") {\n const childrenWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenHeight = Math.max(...childSizes.map((s) => s.height));\n return {\n width: Math.max(nodeWidth, childrenWidth),\n height: nodeHeight + levelGap + childrenHeight,\n };\n } else {\n const childrenHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenWidth = Math.max(...childSizes.map((s) => s.width));\n return {\n width: nodeWidth + levelGap + childrenWidth,\n height: Math.max(nodeHeight, childrenHeight),\n };\n }\n }\n\n // ツリーレイアウトを計算(原点(0,0)からの相対座標)\n function calculateTreeLayout(\n item: TreeDataItem,\n x: number,\n y: number,\n ): LayoutNode {\n const subtreeSize = calculateSubtreeSize(item);\n const layoutNode: LayoutNode = {\n item,\n x: 0,\n y: 0,\n width: nodeWidth,\n height: nodeHeight,\n children: [],\n };\n\n if (layout === \"vertical\") {\n // ノードを中央上部に配置\n layoutNode.x = x + subtreeSize.width / 2 - nodeWidth / 2;\n layoutNode.y = y;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n let childX = x + subtreeSize.width / 2 - totalChildWidth / 2;\n const childY = y + nodeHeight + levelGap;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childX += childSizes[i].width + siblingGap;\n }\n }\n } else {\n // horizontal: ノードを左中央に配置\n layoutNode.x = x;\n layoutNode.y = y + subtreeSize.height / 2 - nodeHeight / 2;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childX = x + nodeWidth + levelGap;\n let childY = y + subtreeSize.height / 2 - totalChildHeight / 2;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childY += childSizes[i].height + siblingGap;\n }\n }\n }\n\n return layoutNode;\n }\n\n // 接続線を描画\n function drawConnector(\n parent: LayoutNode,\n child: LayoutNode,\n style: TreeConnectorStyle,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const lineColor = style.color ?? \"333333\";\n const lineWidth = style.width ?? 2;\n\n if (layout === \"vertical\") {\n // 親の下端中央から子の上端中央へ\n const parentCenterX = ox + (parent.x + parent.width / 2) * sf;\n const parentBottomY = oy + (parent.y + parent.height) * sf;\n const childCenterX = ox + (child.x + child.width / 2) * sf;\n const childTopY = oy + child.y * sf;\n const midY = (parentBottomY + childTopY) / 2;\n\n // 垂直線(親から中間点まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(parentCenterX),\n y: pxToIn(parentBottomY),\n w: 0,\n h: pxToIn(midY - parentBottomY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n\n // 水平線(中間点で)\n const minX = Math.min(parentCenterX, childCenterX);\n const maxX = Math.max(parentCenterX, childCenterX);\n if (maxX > minX) {\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(minX),\n y: pxToIn(midY),\n w: pxToIn(maxX - minX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n\n // 垂直線(中間点から子まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(childCenterX),\n y: pxToIn(midY),\n w: 0,\n h: pxToIn(childTopY - midY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n } else {\n // 親の右端中央から子の左端中央へ\n const parentRightX = ox + (parent.x + parent.width) * sf;\n const parentCenterY = oy + (parent.y + parent.height / 2) * sf;\n const childLeftX = ox + child.x * sf;\n const childCenterY = oy + (child.y + child.height / 2) * sf;\n const midX = (parentRightX + childLeftX) / 2;\n\n // 水平線(親から中間点まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(parentRightX),\n y: pxToIn(parentCenterY),\n w: pxToIn(midX - parentRightX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n\n // 垂直線(中間点で)\n const minY = Math.min(parentCenterY, childCenterY);\n const maxY = Math.max(parentCenterY, childCenterY);\n if (maxY > minY) {\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(midX),\n y: pxToIn(minY),\n w: 0,\n h: pxToIn(maxY - minY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n\n // 水平線(中間点から子まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(midX),\n y: pxToIn(childCenterY),\n w: pxToIn(childLeftX - midX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n }\n\n // ノードを描画\n function drawTreeNode(\n layoutNode: LayoutNode,\n shape: TreeNodeShape,\n defaultNodeColor: string,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const color = layoutNode.item.color ?? defaultNodeColor;\n const shapeType = (() => {\n switch (shape) {\n case \"rect\":\n return ctx.pptx.ShapeType.rect;\n case \"roundRect\":\n return ctx.pptx.ShapeType.roundRect;\n case \"ellipse\":\n return ctx.pptx.ShapeType.ellipse;\n }\n })();\n\n const drawX = ox + layoutNode.x * sf;\n const drawY = oy + layoutNode.y * sf;\n const drawW = layoutNode.width * sf;\n const drawH = layoutNode.height * sf;\n\n // ノードの背景\n ctx.slide.addShape(shapeType, {\n x: pxToIn(drawX),\n y: pxToIn(drawY),\n w: pxToIn(drawW),\n h: pxToIn(drawH),\n fill: { color },\n line: { color: \"333333\", width: pxToPt(1 * sf) },\n });\n\n // ノードのラベル\n ctx.slide.addText(layoutNode.item.label, {\n x: pxToIn(drawX),\n y: pxToIn(drawY),\n w: pxToIn(drawW),\n h: pxToIn(drawH),\n fontSize: pxToPt(12 * sf),\n fontFace: \"Noto Sans JP\",\n color: stripHash(layoutNode.item.textColor) ?? defaultTextColor,\n align: \"center\",\n valign: \"middle\",\n });\n }\n\n // すべての接続線を再帰的に描画\n function drawAllConnectors(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n for (const child of layoutNode.children) {\n drawConnector(layoutNode, child, connectorStyle, sf, ox, oy);\n drawAllConnectors(child, sf, ox, oy);\n }\n }\n\n // すべてのノードを再帰的に描画\n function drawAllNodes(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n drawTreeNode(layoutNode, nodeShape, defaultColor, sf, ox, oy);\n for (const child of layoutNode.children) {\n drawAllNodes(child, sf, ox, oy);\n }\n }\n\n // ツリーのサイズを計算\n const treeSize = calculateSubtreeSize(node.data);\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n treeSize,\n ctx,\n );\n\n // スケール後のサイズで中央配置オフセットを計算\n const scaledW = treeSize.width * scaleFactor;\n const scaledH = treeSize.height * scaleFactor;\n const offsetX = content.x + (content.w - scaledW) / 2;\n const offsetY = content.y + (content.h - scaledH) / 2;\n\n // レイアウト計算(原点(0,0)からの相対座標)\n const rootLayout = calculateTreeLayout(node.data, 0, 0);\n\n // 描画(接続線を先に、ノードを後に描画)\n drawAllConnectors(rootLayout, scaleFactor, offsetX, offsetY);\n drawAllNodes(rootLayout, scaleFactor, offsetX, offsetY);\n}\n"],"mappings":";;;;AAsBA,SAAgB,eACd,MACA,KACM;CACN,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,WAAW,KAAK,YAAY;CAClC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,iBAAiB,KAAK,kBAAkB,CAAC;CAC/C,MAAM,eAAe;CACrB,MAAM,mBAAmB,UAAU,KAAK,SAAS,KAAK;CAGtD,SAAS,qBAAqB,MAG5B;EACA,IAAI,CAAC,KAAK,YAAY,KAAK,SAAS,WAAW,GAC7C,OAAO;GAAE,OAAO;GAAW,QAAQ;EAAW;EAGhD,MAAM,aAAa,KAAK,SAAS,IAAI,oBAAoB;EAEzD,IAAI,WAAW,YAAY;GACzB,MAAM,gBACJ,WAAW,QAAQ,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,IAC9C,cAAc,WAAW,SAAS;GACpC,MAAM,iBAAiB,KAAK,IAAI,GAAG,WAAW,KAAK,MAAM,EAAE,MAAM,CAAC;GAClE,OAAO;IACL,OAAO,KAAK,IAAI,WAAW,aAAa;IACxC,QAAQ,aAAa,WAAW;GAClC;EACF,OAAO;GACL,MAAM,iBACJ,WAAW,QAAQ,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC,IAC/C,cAAc,WAAW,SAAS;GACpC,MAAM,gBAAgB,KAAK,IAAI,GAAG,WAAW,KAAK,MAAM,EAAE,KAAK,CAAC;GAChE,OAAO;IACL,OAAO,YAAY,WAAW;IAC9B,QAAQ,KAAK,IAAI,YAAY,cAAc;GAC7C;EACF;CACF;CAGA,SAAS,oBACP,MACA,GACA,GACY;EACZ,MAAM,cAAc,qBAAqB,IAAI;EAC7C,MAAM,aAAyB;GAC7B;GACA,GAAG;GACH,GAAG;GACH,OAAO;GACP,QAAQ;GACR,UAAU,CAAC;EACb;EAEA,IAAI,WAAW,YAAY;GAEzB,WAAW,IAAI,IAAI,YAAY,QAAQ,IAAI,YAAY;GACvD,WAAW,IAAI;GAGf,IAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;IAC7C,MAAM,aAAa,KAAK,SAAS,IAAI,oBAAoB;IACzD,MAAM,kBACJ,WAAW,QAAQ,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,IAC9C,cAAc,WAAW,SAAS;IACpC,IAAI,SAAS,IAAI,YAAY,QAAQ,IAAI,kBAAkB;IAC3D,MAAM,SAAS,IAAI,aAAa;IAEhC,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;KAC7C,MAAM,QAAQ,KAAK,SAAS;KAC5B,MAAM,cAAc,oBAAoB,OAAO,QAAQ,MAAM;KAC7D,WAAW,SAAS,KAAK,WAAW;KACpC,UAAU,WAAW,EAAE,CAAC,QAAQ;IAClC;GACF;EACF,OAAO;GAEL,WAAW,IAAI;GACf,WAAW,IAAI,IAAI,YAAY,SAAS,IAAI,aAAa;GAGzD,IAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;IAC7C,MAAM,aAAa,KAAK,SAAS,IAAI,oBAAoB;IACzD,MAAM,mBACJ,WAAW,QAAQ,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC,IAC/C,cAAc,WAAW,SAAS;IACpC,MAAM,SAAS,IAAI,YAAY;IAC/B,IAAI,SAAS,IAAI,YAAY,SAAS,IAAI,mBAAmB;IAE7D,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;KAC7C,MAAM,QAAQ,KAAK,SAAS;KAC5B,MAAM,cAAc,oBAAoB,OAAO,QAAQ,MAAM;KAC7D,WAAW,SAAS,KAAK,WAAW;KACpC,UAAU,WAAW,EAAE,CAAC,SAAS;IACnC;GACF;EACF;EAEA,OAAO;CACT;CAGA,SAAS,cACP,QACA,OACA,OACA,IACA,IACA,IACA;EACA,MAAM,YAAY,MAAM,SAAS;EACjC,MAAM,YAAY,MAAM,SAAS;EAEjC,IAAI,WAAW,YAAY;GAEzB,MAAM,gBAAgB,MAAM,OAAO,IAAI,OAAO,QAAQ,KAAK;GAC3D,MAAM,gBAAgB,MAAM,OAAO,IAAI,OAAO,UAAU;GACxD,MAAM,eAAe,MAAM,MAAM,IAAI,MAAM,QAAQ,KAAK;GACxD,MAAM,YAAY,KAAK,MAAM,IAAI;GACjC,MAAM,QAAQ,gBAAgB,aAAa;GAG3C,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,MAAM;IAC1C,GAAG,OAAO,aAAa;IACvB,GAAG,OAAO,aAAa;IACvB,GAAG;IACH,GAAG,OAAO,OAAO,aAAa;IAC9B,MAAM;KAAE,OAAO;KAAW,OAAO,OAAO,YAAY,EAAE;IAAE;GAC1D,CAAC;GAGD,MAAM,OAAO,KAAK,IAAI,eAAe,YAAY;GACjD,MAAM,OAAO,KAAK,IAAI,eAAe,YAAY;GACjD,IAAI,OAAO,MACT,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,MAAM;IAC1C,GAAG,OAAO,IAAI;IACd,GAAG,OAAO,IAAI;IACd,GAAG,OAAO,OAAO,IAAI;IACrB,GAAG;IACH,MAAM;KAAE,OAAO;KAAW,OAAO,OAAO,YAAY,EAAE;IAAE;GAC1D,CAAC;GAIH,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,MAAM;IAC1C,GAAG,OAAO,YAAY;IACtB,GAAG,OAAO,IAAI;IACd,GAAG;IACH,GAAG,OAAO,YAAY,IAAI;IAC1B,MAAM;KAAE,OAAO;KAAW,OAAO,OAAO,YAAY,EAAE;IAAE;GAC1D,CAAC;EACH,OAAO;GAEL,MAAM,eAAe,MAAM,OAAO,IAAI,OAAO,SAAS;GACtD,MAAM,gBAAgB,MAAM,OAAO,IAAI,OAAO,SAAS,KAAK;GAC5D,MAAM,aAAa,KAAK,MAAM,IAAI;GAClC,MAAM,eAAe,MAAM,MAAM,IAAI,MAAM,SAAS,KAAK;GACzD,MAAM,QAAQ,eAAe,cAAc;GAG3C,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,MAAM;IAC1C,GAAG,OAAO,YAAY;IACtB,GAAG,OAAO,aAAa;IACvB,GAAG,OAAO,OAAO,YAAY;IAC7B,GAAG;IACH,MAAM;KAAE,OAAO;KAAW,OAAO,OAAO,YAAY,EAAE;IAAE;GAC1D,CAAC;GAGD,MAAM,OAAO,KAAK,IAAI,eAAe,YAAY;GACjD,MAAM,OAAO,KAAK,IAAI,eAAe,YAAY;GACjD,IAAI,OAAO,MACT,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,MAAM;IAC1C,GAAG,OAAO,IAAI;IACd,GAAG,OAAO,IAAI;IACd,GAAG;IACH,GAAG,OAAO,OAAO,IAAI;IACrB,MAAM;KAAE,OAAO;KAAW,OAAO,OAAO,YAAY,EAAE;IAAE;GAC1D,CAAC;GAIH,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,MAAM;IAC1C,GAAG,OAAO,IAAI;IACd,GAAG,OAAO,YAAY;IACtB,GAAG,OAAO,aAAa,IAAI;IAC3B,GAAG;IACH,MAAM;KAAE,OAAO;KAAW,OAAO,OAAO,YAAY,EAAE;IAAE;GAC1D,CAAC;EACH;CACF;CAGA,SAAS,aACP,YACA,OACA,kBACA,IACA,IACA,IACA;EACA,MAAM,QAAQ,WAAW,KAAK,SAAS;EACvC,MAAM,mBAAmB;GACvB,QAAQ,OAAR;IACE,KAAK,QACH,OAAO,IAAI,KAAK,UAAU;IAC5B,KAAK,aACH,OAAO,IAAI,KAAK,UAAU;IAC5B,KAAK,WACH,OAAO,IAAI,KAAK,UAAU;GAC9B;EACF,EAAA,CAAG;EAEH,MAAM,QAAQ,KAAK,WAAW,IAAI;EAClC,MAAM,QAAQ,KAAK,WAAW,IAAI;EAClC,MAAM,QAAQ,WAAW,QAAQ;EACjC,MAAM,QAAQ,WAAW,SAAS;EAGlC,IAAI,MAAM,SAAS,WAAW;GAC5B,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,KAAK;GACf,MAAM,EAAE,MAAM;GACd,MAAM;IAAE,OAAO;IAAU,OAAO,OAAO,IAAI,EAAE;GAAE;EACjD,CAAC;EAGD,IAAI,MAAM,QAAQ,WAAW,KAAK,OAAO;GACvC,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,KAAK;GACf,UAAU,OAAO,KAAK,EAAE;GACxB,UAAU;GACV,OAAO,UAAU,WAAW,KAAK,SAAS,KAAK;GAC/C,OAAO;GACP,QAAQ;EACV,CAAC;CACH;CAGA,SAAS,kBACP,YACA,IACA,IACA,IACA;EACA,KAAK,MAAM,SAAS,WAAW,UAAU;GACvC,cAAc,YAAY,OAAO,gBAAgB,IAAI,IAAI,EAAE;GAC3D,kBAAkB,OAAO,IAAI,IAAI,EAAE;EACrC;CACF;CAGA,SAAS,aACP,YACA,IACA,IACA,IACA;EACA,aAAa,YAAY,WAAW,cAAc,IAAI,IAAI,EAAE;EAC5D,KAAK,MAAM,SAAS,WAAW,UAC7B,aAAa,OAAO,IAAI,IAAI,EAAE;CAElC;CAGA,MAAM,WAAW,qBAAqB,KAAK,IAAI;CAG/C,MAAM,EAAE,SAAS,gBAAgB,yBAC/B,MACA,UACA,GACF;CAGA,MAAM,UAAU,SAAS,QAAQ;CACjC,MAAM,UAAU,SAAS,SAAS;CAClC,MAAM,UAAU,QAAQ,KAAK,QAAQ,IAAI,WAAW;CACpD,MAAM,UAAU,QAAQ,KAAK,QAAQ,IAAI,WAAW;CAGpD,MAAM,aAAa,oBAAoB,KAAK,MAAM,GAAG,CAAC;CAGtD,kBAAkB,YAAY,aAAa,SAAS,OAAO;CAC3D,aAAa,YAAY,aAAa,SAAS,OAAO;AACxD"}
1
+ {"version":3,"file":"tree.js","names":[],"sources":["../../../src/renderPptx/nodes/tree.ts"],"sourcesContent":["import type {\n PositionedNode,\n TreeDataItem,\n TreeNodeShape,\n TreeConnectorStyle,\n} from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport { addStraightLine } from \"../utils/straightLine.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n shapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { createGlimpseParagraphs } from \"../utils/glimpseTextBox.ts\";\n\ntype TreePositionedNode = Extract<PositionedNode, { type: \"tree\" }>;\n\ninterface LayoutNode {\n item: TreeDataItem;\n x: number;\n y: number;\n width: number;\n height: number;\n children: LayoutNode[];\n}\n\nexport function renderTreeNode(\n node: TreePositionedNode,\n ctx: RenderContext,\n): void {\n const layout = node.layout ?? \"vertical\";\n const nodeShape = node.nodeShape ?? \"rect\";\n const nodeWidth = node.nodeWidth ?? 120;\n const nodeHeight = node.nodeHeight ?? 40;\n const levelGap = node.levelGap ?? 60;\n const siblingGap = node.siblingGap ?? 20;\n const connectorStyle = node.connectorStyle ?? {};\n const defaultColor = \"1D4ED8\";\n const defaultTextColor = stripHash(node.textColor) ?? \"FFFFFF\";\n\n // サブツリーの幅/高さを計算\n function calculateSubtreeSize(item: TreeDataItem): {\n width: number;\n height: number;\n } {\n if (!item.children || item.children.length === 0) {\n return { width: nodeWidth, height: nodeHeight };\n }\n\n const childSizes = item.children.map(calculateSubtreeSize);\n\n if (layout === \"vertical\") {\n const childrenWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenHeight = Math.max(...childSizes.map((s) => s.height));\n return {\n width: Math.max(nodeWidth, childrenWidth),\n height: nodeHeight + levelGap + childrenHeight,\n };\n } else {\n const childrenHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenWidth = Math.max(...childSizes.map((s) => s.width));\n return {\n width: nodeWidth + levelGap + childrenWidth,\n height: Math.max(nodeHeight, childrenHeight),\n };\n }\n }\n\n // ツリーレイアウトを計算(原点(0,0)からの相対座標)\n function calculateTreeLayout(\n item: TreeDataItem,\n x: number,\n y: number,\n ): LayoutNode {\n const subtreeSize = calculateSubtreeSize(item);\n const layoutNode: LayoutNode = {\n item,\n x: 0,\n y: 0,\n width: nodeWidth,\n height: nodeHeight,\n children: [],\n };\n\n if (layout === \"vertical\") {\n // ノードを中央上部に配置\n layoutNode.x = x + subtreeSize.width / 2 - nodeWidth / 2;\n layoutNode.y = y;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n let childX = x + subtreeSize.width / 2 - totalChildWidth / 2;\n const childY = y + nodeHeight + levelGap;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childX += childSizes[i].width + siblingGap;\n }\n }\n } else {\n // horizontal: ノードを左中央に配置\n layoutNode.x = x;\n layoutNode.y = y + subtreeSize.height / 2 - nodeHeight / 2;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childX = x + nodeWidth + levelGap;\n let childY = y + subtreeSize.height / 2 - totalChildHeight / 2;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childY += childSizes[i].height + siblingGap;\n }\n }\n }\n\n return layoutNode;\n }\n\n // 接続線を描画\n function drawConnector(\n parent: LayoutNode,\n child: LayoutNode,\n style: TreeConnectorStyle,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const lineColor = style.color ?? \"333333\";\n const lineWidth = style.width ?? 2;\n\n if (layout === \"vertical\") {\n // 親の下端中央から子の上端中央へ\n const parentCenterX = ox + (parent.x + parent.width / 2) * sf;\n const parentBottomY = oy + (parent.y + parent.height) * sf;\n const childCenterX = ox + (child.x + child.width / 2) * sf;\n const childTopY = oy + child.y * sf;\n const midY = (parentBottomY + childTopY) / 2;\n\n // 垂直線(親から中間点まで)\n addStraightLine(\n ctx,\n {\n x1: parentCenterX,\n y1: parentBottomY,\n x2: parentCenterX,\n y2: midY,\n },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n\n // 水平線(中間点で)\n const minX = Math.min(parentCenterX, childCenterX);\n const maxX = Math.max(parentCenterX, childCenterX);\n if (maxX > minX) {\n addStraightLine(\n ctx,\n { x1: minX, y1: midY, x2: maxX, y2: midY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n }\n\n // 垂直線(中間点から子まで)\n addStraightLine(\n ctx,\n { x1: childCenterX, y1: midY, x2: childCenterX, y2: childTopY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n } else {\n // 親の右端中央から子の左端中央へ\n const parentRightX = ox + (parent.x + parent.width) * sf;\n const parentCenterY = oy + (parent.y + parent.height / 2) * sf;\n const childLeftX = ox + child.x * sf;\n const childCenterY = oy + (child.y + child.height / 2) * sf;\n const midX = (parentRightX + childLeftX) / 2;\n\n // 水平線(親から中間点まで)\n addStraightLine(\n ctx,\n { x1: parentRightX, y1: parentCenterY, x2: midX, y2: parentCenterY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n\n // 垂直線(中間点で)\n const minY = Math.min(parentCenterY, childCenterY);\n const maxY = Math.max(parentCenterY, childCenterY);\n if (maxY > minY) {\n addStraightLine(\n ctx,\n { x1: midX, y1: minY, x2: midX, y2: maxY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n }\n\n // 水平線(中間点から子まで)\n addStraightLine(\n ctx,\n { x1: midX, y1: childCenterY, x2: childLeftX, y2: childCenterY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n }\n }\n\n // ノードを描画\n function drawTreeNode(\n layoutNode: LayoutNode,\n shape: TreeNodeShape,\n defaultNodeColor: string,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const color = layoutNode.item.color ?? defaultNodeColor;\n const shapeType = (() => {\n switch (shape) {\n case \"rect\":\n return \"rect\";\n case \"roundRect\":\n return \"roundRect\";\n case \"ellipse\":\n return \"ellipse\";\n }\n })();\n\n const drawX = ox + layoutNode.x * sf;\n const drawY = oy + layoutNode.y * sf;\n const drawW = layoutNode.width * sf;\n const drawH = layoutNode.height * sf;\n\n addGlimpseShape(\n ctx,\n {\n preset: shapeType,\n ...createShapeBoundsInput({ x: drawX, y: drawY, w: drawW, h: drawH }),\n fill: solidShapeFill(color),\n outline: shapeOutline({ color: \"333333\", width: 1 * sf }),\n body: { anchor: \"middle\" },\n paragraphs: createGlimpseParagraphs(\n layoutNode.item.label,\n {\n fontSize: 12 * sf,\n fontFace: \"Noto Sans JP\",\n color: stripHash(layoutNode.item.textColor) ?? defaultTextColor,\n },\n { align: \"center\" },\n ),\n },\n { x: drawX, y: drawY, w: drawW, h: drawH },\n { fillColor: color },\n );\n }\n\n // すべての接続線を再帰的に描画\n function drawAllConnectors(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n for (const child of layoutNode.children) {\n drawConnector(layoutNode, child, connectorStyle, sf, ox, oy);\n drawAllConnectors(child, sf, ox, oy);\n }\n }\n\n // すべてのノードを再帰的に描画\n function drawAllNodes(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n drawTreeNode(layoutNode, nodeShape, defaultColor, sf, ox, oy);\n for (const child of layoutNode.children) {\n drawAllNodes(child, sf, ox, oy);\n }\n }\n\n // ツリーのサイズを計算\n const treeSize = calculateSubtreeSize(node.data);\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n treeSize,\n ctx,\n );\n\n // スケール後のサイズで中央配置オフセットを計算\n const scaledW = treeSize.width * scaleFactor;\n const scaledH = treeSize.height * scaleFactor;\n const offsetX = content.x + (content.w - scaledW) / 2;\n const offsetY = content.y + (content.h - scaledH) / 2;\n\n // レイアウト計算(原点(0,0)からの相対座標)\n const rootLayout = calculateTreeLayout(node.data, 0, 0);\n\n // 描画(接続線を先に、ノードを後に描画)\n drawAllConnectors(rootLayout, scaleFactor, offsetX, offsetY);\n drawAllNodes(rootLayout, scaleFactor, offsetX, offsetY);\n}\n"],"mappings":";;;;;;AA6BA,SAAgB,eACd,MACA,KACM;CACN,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,WAAW,KAAK,YAAY;CAClC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,iBAAiB,KAAK,kBAAkB,CAAC;CAC/C,MAAM,eAAe;CACrB,MAAM,mBAAmB,UAAU,KAAK,SAAS,KAAK;CAGtD,SAAS,qBAAqB,MAG5B;EACA,IAAI,CAAC,KAAK,YAAY,KAAK,SAAS,WAAW,GAC7C,OAAO;GAAE,OAAO;GAAW,QAAQ;EAAW;EAGhD,MAAM,aAAa,KAAK,SAAS,IAAI,oBAAoB;EAEzD,IAAI,WAAW,YAAY;GACzB,MAAM,gBACJ,WAAW,QAAQ,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,IAC9C,cAAc,WAAW,SAAS;GACpC,MAAM,iBAAiB,KAAK,IAAI,GAAG,WAAW,KAAK,MAAM,EAAE,MAAM,CAAC;GAClE,OAAO;IACL,OAAO,KAAK,IAAI,WAAW,aAAa;IACxC,QAAQ,aAAa,WAAW;GAClC;EACF,OAAO;GACL,MAAM,iBACJ,WAAW,QAAQ,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC,IAC/C,cAAc,WAAW,SAAS;GACpC,MAAM,gBAAgB,KAAK,IAAI,GAAG,WAAW,KAAK,MAAM,EAAE,KAAK,CAAC;GAChE,OAAO;IACL,OAAO,YAAY,WAAW;IAC9B,QAAQ,KAAK,IAAI,YAAY,cAAc;GAC7C;EACF;CACF;CAGA,SAAS,oBACP,MACA,GACA,GACY;EACZ,MAAM,cAAc,qBAAqB,IAAI;EAC7C,MAAM,aAAyB;GAC7B;GACA,GAAG;GACH,GAAG;GACH,OAAO;GACP,QAAQ;GACR,UAAU,CAAC;EACb;EAEA,IAAI,WAAW,YAAY;GAEzB,WAAW,IAAI,IAAI,YAAY,QAAQ,IAAI,YAAY;GACvD,WAAW,IAAI;GAGf,IAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;IAC7C,MAAM,aAAa,KAAK,SAAS,IAAI,oBAAoB;IACzD,MAAM,kBACJ,WAAW,QAAQ,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,IAC9C,cAAc,WAAW,SAAS;IACpC,IAAI,SAAS,IAAI,YAAY,QAAQ,IAAI,kBAAkB;IAC3D,MAAM,SAAS,IAAI,aAAa;IAEhC,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;KAC7C,MAAM,QAAQ,KAAK,SAAS;KAC5B,MAAM,cAAc,oBAAoB,OAAO,QAAQ,MAAM;KAC7D,WAAW,SAAS,KAAK,WAAW;KACpC,UAAU,WAAW,EAAE,CAAC,QAAQ;IAClC;GACF;EACF,OAAO;GAEL,WAAW,IAAI;GACf,WAAW,IAAI,IAAI,YAAY,SAAS,IAAI,aAAa;GAGzD,IAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;IAC7C,MAAM,aAAa,KAAK,SAAS,IAAI,oBAAoB;IACzD,MAAM,mBACJ,WAAW,QAAQ,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC,IAC/C,cAAc,WAAW,SAAS;IACpC,MAAM,SAAS,IAAI,YAAY;IAC/B,IAAI,SAAS,IAAI,YAAY,SAAS,IAAI,mBAAmB;IAE7D,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;KAC7C,MAAM,QAAQ,KAAK,SAAS;KAC5B,MAAM,cAAc,oBAAoB,OAAO,QAAQ,MAAM;KAC7D,WAAW,SAAS,KAAK,WAAW;KACpC,UAAU,WAAW,EAAE,CAAC,SAAS;IACnC;GACF;EACF;EAEA,OAAO;CACT;CAGA,SAAS,cACP,QACA,OACA,OACA,IACA,IACA,IACA;EACA,MAAM,YAAY,MAAM,SAAS;EACjC,MAAM,YAAY,MAAM,SAAS;EAEjC,IAAI,WAAW,YAAY;GAEzB,MAAM,gBAAgB,MAAM,OAAO,IAAI,OAAO,QAAQ,KAAK;GAC3D,MAAM,gBAAgB,MAAM,OAAO,IAAI,OAAO,UAAU;GACxD,MAAM,eAAe,MAAM,MAAM,IAAI,MAAM,QAAQ,KAAK;GACxD,MAAM,YAAY,KAAK,MAAM,IAAI;GACjC,MAAM,QAAQ,gBAAgB,aAAa;GAG3C,gBACE,KACA;IACE,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,GACA;IAAE,OAAO;IAAW,WAAW,YAAY;GAAG,CAChD;GAGA,MAAM,OAAO,KAAK,IAAI,eAAe,YAAY;GACjD,MAAM,OAAO,KAAK,IAAI,eAAe,YAAY;GACjD,IAAI,OAAO,MACT,gBACE,KACA;IAAE,IAAI;IAAM,IAAI;IAAM,IAAI;IAAM,IAAI;GAAK,GACzC;IAAE,OAAO;IAAW,WAAW,YAAY;GAAG,CAChD;GAIF,gBACE,KACA;IAAE,IAAI;IAAc,IAAI;IAAM,IAAI;IAAc,IAAI;GAAU,GAC9D;IAAE,OAAO;IAAW,WAAW,YAAY;GAAG,CAChD;EACF,OAAO;GAEL,MAAM,eAAe,MAAM,OAAO,IAAI,OAAO,SAAS;GACtD,MAAM,gBAAgB,MAAM,OAAO,IAAI,OAAO,SAAS,KAAK;GAC5D,MAAM,aAAa,KAAK,MAAM,IAAI;GAClC,MAAM,eAAe,MAAM,MAAM,IAAI,MAAM,SAAS,KAAK;GACzD,MAAM,QAAQ,eAAe,cAAc;GAG3C,gBACE,KACA;IAAE,IAAI;IAAc,IAAI;IAAe,IAAI;IAAM,IAAI;GAAc,GACnE;IAAE,OAAO;IAAW,WAAW,YAAY;GAAG,CAChD;GAGA,MAAM,OAAO,KAAK,IAAI,eAAe,YAAY;GACjD,MAAM,OAAO,KAAK,IAAI,eAAe,YAAY;GACjD,IAAI,OAAO,MACT,gBACE,KACA;IAAE,IAAI;IAAM,IAAI;IAAM,IAAI;IAAM,IAAI;GAAK,GACzC;IAAE,OAAO;IAAW,WAAW,YAAY;GAAG,CAChD;GAIF,gBACE,KACA;IAAE,IAAI;IAAM,IAAI;IAAc,IAAI;IAAY,IAAI;GAAa,GAC/D;IAAE,OAAO;IAAW,WAAW,YAAY;GAAG,CAChD;EACF;CACF;CAGA,SAAS,aACP,YACA,OACA,kBACA,IACA,IACA,IACA;EACA,MAAM,QAAQ,WAAW,KAAK,SAAS;EACvC,MAAM,mBAAmB;GACvB,QAAQ,OAAR;IACE,KAAK,QACH,OAAO;IACT,KAAK,aACH,OAAO;IACT,KAAK,WACH,OAAO;GACX;EACF,EAAA,CAAG;EAEH,MAAM,QAAQ,KAAK,WAAW,IAAI;EAClC,MAAM,QAAQ,KAAK,WAAW,IAAI;EAClC,MAAM,QAAQ,WAAW,QAAQ;EACjC,MAAM,QAAQ,WAAW,SAAS;EAElC,gBACE,KACA;GACE,QAAQ;GACR,GAAG,uBAAuB;IAAE,GAAG;IAAO,GAAG;IAAO,GAAG;IAAO,GAAG;GAAM,CAAC;GACpE,MAAM,eAAe,KAAK;GAC1B,SAAS,aAAa;IAAE,OAAO;IAAU,OAAO,IAAI;GAAG,CAAC;GACxD,MAAM,EAAE,QAAQ,SAAS;GACzB,YAAY,wBACV,WAAW,KAAK,OAChB;IACE,UAAU,KAAK;IACf,UAAU;IACV,OAAO,UAAU,WAAW,KAAK,SAAS,KAAK;GACjD,GACA,EAAE,OAAO,SAAS,CACpB;EACF,GACA;GAAE,GAAG;GAAO,GAAG;GAAO,GAAG;GAAO,GAAG;EAAM,GACzC,EAAE,WAAW,MAAM,CACrB;CACF;CAGA,SAAS,kBACP,YACA,IACA,IACA,IACA;EACA,KAAK,MAAM,SAAS,WAAW,UAAU;GACvC,cAAc,YAAY,OAAO,gBAAgB,IAAI,IAAI,EAAE;GAC3D,kBAAkB,OAAO,IAAI,IAAI,EAAE;EACrC;CACF;CAGA,SAAS,aACP,YACA,IACA,IACA,IACA;EACA,aAAa,YAAY,WAAW,cAAc,IAAI,IAAI,EAAE;EAC5D,KAAK,MAAM,SAAS,WAAW,UAC7B,aAAa,OAAO,IAAI,IAAI,EAAE;CAElC;CAGA,MAAM,WAAW,qBAAqB,KAAK,IAAI;CAG/C,MAAM,EAAE,SAAS,gBAAgB,yBAC/B,MACA,UACA,GACF;CAGA,MAAM,UAAU,SAAS,QAAQ;CACjC,MAAM,UAAU,SAAS,SAAS;CAClC,MAAM,UAAU,QAAQ,KAAK,QAAQ,IAAI,WAAW;CACpD,MAAM,UAAU,QAAQ,KAAK,QAAQ,IAAI,WAAW;CAGpD,MAAM,aAAa,oBAAoB,KAAK,MAAM,GAAG,CAAC;CAGtD,kBAAkB,YAAY,aAAa,SAAS,OAAO;CAC3D,aAAa,YAAY,aAAa,SAAS,OAAO;AACxD"}
@@ -3,7 +3,6 @@ import { resolveBoxSpacing } from "../shared/boxSpacing.js";
3
3
  import { getNodeDef } from "../registry/nodeRegistry.js";
4
4
  import { pxToIn, pxToPt } from "./units.js";
5
5
  import { convertStrike, convertUnderline } from "./textOptions.js";
6
- import { registerBackgroundGradient } from "./gradientFills.js";
7
6
  import "../registry/index.js";
8
7
  import { renderBackgroundAndBorder, renderBorderOnly } from "./utils/backgroundBorder.js";
9
8
  //#region src/renderPptx/renderPptx.ts
@@ -154,7 +153,7 @@ async function renderPptx(pages, slidePx, buildContext, master) {
154
153
  });
155
154
  pptx.layout = "custom";
156
155
  const masterName = master ? defineSlideMasterFromOptions(pptx, master) : void 0;
157
- for (const data of pages) {
156
+ for (const [pageIndex, data] of pages.entries()) {
158
157
  const slide = masterName ? pptx.addSlide({ masterName }) : pptx.addSlide();
159
158
  const ctx = {
160
159
  slide,
@@ -166,9 +165,9 @@ async function renderPptx(pages, slidePx, buildContext, master) {
166
165
  const rootBackgroundColor = !isLinelike ? data.backgroundColor : void 0;
167
166
  const rootBackgroundGradient = !isLinelike ? data.backgroundGradient : void 0;
168
167
  const rootHasOpacity = !isLinelike && "opacity" in data && data.opacity !== void 0;
169
- const rootGradientMarker = rootBackgroundGradient && !rootHasOpacity ? registerBackgroundGradient(rootBackgroundGradient, void 0, buildContext.gradientFills) : void 0;
168
+ const rootGradientMarker = rootBackgroundGradient && !rootHasOpacity ? buildContext.glimpseTextBoxes.registerSlideBackgroundGradient(rootBackgroundGradient, pageIndex + 1) : void 0;
170
169
  if (rootGradientMarker) slide.background = { color: rootGradientMarker };
171
- else if (rootBackgroundColor && !rootHasOpacity) slide.background = { color: rootBackgroundColor };
170
+ else if (rootBackgroundColor && !rootBackgroundGradient && !rootHasOpacity) slide.background = { color: rootBackgroundColor };
172
171
  const rootBackgroundImage = !isLinelike ? data.backgroundImage : void 0;
173
172
  if (rootBackgroundImage) {
174
173
  const cachedData = getImageData(rootBackgroundImage.src, buildContext.imageDataCache);
@@ -180,7 +179,7 @@ async function renderPptx(pages, slidePx, buildContext, master) {
180
179
  * @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)
181
180
  */
182
181
  function renderNode(node, isRoot = false) {
183
- if (node.type !== "line" && node.type !== "arrow") if (isRoot && (rootBackgroundImage || (rootBackgroundColor || rootBackgroundGradient) && !rootHasOpacity)) renderBorderOnly(node, ctx);
182
+ if (node.type !== "line" && node.type !== "arrow") if (isRoot && (rootBackgroundImage || (rootBackgroundColor || rootGradientMarker) && !rootHasOpacity)) renderBorderOnly(node, ctx);
184
183
  else renderBackgroundAndBorder(node, ctx);
185
184
  const def = getNodeDef(node.type);
186
185
  switch (def.category) {