pptx-viewer-core 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@ All notable changes to this project are documented here.
4
4
  This file is generated from [Conventional Commits](https://www.conventionalcommits.org)
5
5
  by [git-cliff](https://git-cliff.org); do not edit it by hand.
6
6
 
7
+ ## [1.2.3](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-viewer-core@1.2.3) - 2026-07-08
8
+
9
+ ### Documentation
10
+
11
+ - **core:** Remove explicit jszip/fast-xml-parser mention from install section (by @ChristopherVR) ([6b72906](https://github.com/ChristopherVR/pptx-viewer/commit/6b72906c08447ba38a704ff4572c89d7cad7e60c))
12
+
13
+ ## [1.2.2](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-viewer-core@1.2.2) - 2026-07-07
14
+
7
15
  ## [1.2.1](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-viewer-core@1.2.1) - 2026-07-06
8
16
 
9
17
  ## [1.2.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-viewer-core@1.2.0) - 2026-07-05
package/README.md CHANGED
@@ -20,7 +20,7 @@ There is no UI here: this is the engine on its own. Use it directly when you nee
20
20
  npm install pptx-viewer-core
21
21
  ```
22
22
 
23
- > That's it, nothing else to install. **jszip** (a `.pptx` is a ZIP file, and this reads and writes that container) and **fast-xml-parser** (the slides inside are XML, and this reads and writes it) are regular dependencies of `pptx-viewer-core`, so your package manager pulls them in automatically. Password protection and digital signatures need a few extra packages (`node-forge`, `xml-crypto`, `@xmldom/xmldom`), but only if you actually use those features.
23
+ > That's it, nothing else to install. All runtime dependencies are pulled in automatically by your package manager. Password protection and digital signatures need a few extra packages (`node-forge`, `xml-crypto`, `@xmldom/xmldom`), but only if you actually use those features.
24
24
 
25
25
  ## What it does
26
26
 
package/dist/cli/index.js CHANGED
Binary file
Binary file
package/dist/index.d.mts CHANGED
@@ -2594,7 +2594,11 @@ declare function addSmartArtNode(data: PptxSmartArtData, text: string, afterNode
2594
2594
  declare function removeSmartArtNode(data: PptxSmartArtData, nodeId: string): PptxSmartArtData;
2595
2595
  /**
2596
2596
  * Update the text of a SmartArt node by ID.
2597
- * Clears drawing shapes to trigger layout reflow.
2597
+ *
2598
+ * When drawing shapes exist, updates the matching shape's text in-place so the
2599
+ * renderer path does not change (avoids polygon-to-chevron downgrade that makes
2600
+ * a pyramid look like stacked bars). When no drawing shapes are present the
2601
+ * family renderer reads text directly from the node array.
2598
2602
  */
2599
2603
  declare function updateSmartArtNodeText(data: PptxSmartArtData, nodeId: string, newText: string): PptxSmartArtData;
2600
2604
  /**
package/dist/index.d.ts CHANGED
@@ -2594,7 +2594,11 @@ declare function addSmartArtNode(data: PptxSmartArtData, text: string, afterNode
2594
2594
  declare function removeSmartArtNode(data: PptxSmartArtData, nodeId: string): PptxSmartArtData;
2595
2595
  /**
2596
2596
  * Update the text of a SmartArt node by ID.
2597
- * Clears drawing shapes to trigger layout reflow.
2597
+ *
2598
+ * When drawing shapes exist, updates the matching shape's text in-place so the
2599
+ * renderer path does not change (avoids polygon-to-chevron downgrade that makes
2600
+ * a pyramid look like stacked bars). When no drawing shapes are present the
2601
+ * family renderer reads text directly from the node array.
2598
2602
  */
2599
2603
  declare function updateSmartArtNodeText(data: PptxSmartArtData, nodeId: string, newText: string): PptxSmartArtData;
2600
2604
  /**
package/dist/index.js CHANGED
@@ -16907,6 +16907,7 @@ function getDefaultShapeType(layoutType) {
16907
16907
  case "cycle":
16908
16908
  case "target":
16909
16909
  case "gear":
16910
+ return "ellipse";
16910
16911
  case "relationship":
16911
16912
  case "venn":
16912
16913
  return "ellipse";
@@ -16914,7 +16915,10 @@ function getDefaultShapeType(layoutType) {
16914
16915
  return "chevron";
16915
16916
  case "pyramid":
16916
16917
  case "funnel":
16917
- return "rect";
16918
+ return "trapezoid";
16919
+ case "timeline":
16920
+ case "bending":
16921
+ return "roundRect";
16918
16922
  default:
16919
16923
  return "roundRect";
16920
16924
  }
@@ -17004,7 +17008,7 @@ function layoutCycle(nodes, bounds, themeColorMap) {
17004
17008
  const ny = cy + radius * Math.sin(angle) - nodeH / 2;
17005
17009
  const fill = accentColor(i, themeColorMap);
17006
17010
  elements.push(
17007
- makeShapeElement(nextId("sa-cycle"), nx, ny, nodeW, nodeH, "roundRect", fill, node.text, {
17011
+ makeShapeElement(nextId("sa-cycle"), nx, ny, nodeW, nodeH, "ellipse", fill, node.text, {
17008
17012
  fontSize: Math.max(7, Math.min(10, nodeW * 0.1))
17009
17013
  })
17010
17014
  );
@@ -17071,7 +17075,7 @@ function layoutPyramid(nodes, bounds, themeColorMap) {
17071
17075
  const x = bounds.x + (bounds.width - w) / 2;
17072
17076
  const y = bounds.y + padding + i * (bandH + gap);
17073
17077
  const fill = accentColor(i, themeColorMap);
17074
- return makeShapeElement(nextId("sa-pyramid"), x, y, w, bandH, "rect", fill, node.text, {
17078
+ return makeShapeElement(nextId("sa-pyramid"), x, y, w, bandH, "trapezoid", fill, node.text, {
17075
17079
  fontSize: Math.max(8, Math.min(11, bandH * 0.4))
17076
17080
  });
17077
17081
  });
@@ -17820,7 +17824,7 @@ function decomposeSmartArt(smartArtData, containerBounds, themeColorMap, layoutD
17820
17824
  themeColorMap,
17821
17825
  smartArtData.colorTransform?.fillColors
17822
17826
  );
17823
- const layoutType = smartArtData.resolvedLayoutType ?? resolveLayoutFromRawType(smartArtData.layoutType);
17827
+ const layoutType = resolveEffectiveLayoutType(smartArtData);
17824
17828
  const namedLayout = smartArtData.layout;
17825
17829
  if (namedLayout) {
17826
17830
  const namedResult = dispatchNamedLayout(namedLayout, nodes, containerBounds, effectiveThemeMap);
@@ -17851,6 +17855,52 @@ function dispatchLayoutByType(layoutType, nodes, containerBounds, effectiveTheme
17851
17855
  return layoutWithHeuristic(nodes, containerBounds, effectiveThemeMap);
17852
17856
  }
17853
17857
  }
17858
+ var LAYOUT_PRESET_TO_TYPE = {
17859
+ basicBlockList: "list",
17860
+ alternatingHexagons: "list",
17861
+ horizontalBulletList: "list",
17862
+ stackedList: "list",
17863
+ tableList: "list",
17864
+ trapezoidList: "list",
17865
+ pictureAccentList: "list",
17866
+ verticalBlockList: "list",
17867
+ groupedList: "list",
17868
+ pyramidList: "list",
17869
+ horizontalPictureList: "list",
17870
+ basicMatrix: "matrix",
17871
+ basicPyramid: "pyramid",
17872
+ invertedPyramid: "pyramid",
17873
+ basicChevronProcess: "chevron",
17874
+ continuousBlockProcess: "process",
17875
+ segmentedProcess: "process",
17876
+ upwardArrow: "process",
17877
+ basicTimeline: "timeline",
17878
+ bendingProcess: "bending",
17879
+ stepDownProcess: "process",
17880
+ alternatingFlow: "process",
17881
+ descendingProcess: "process",
17882
+ accentProcess: "process",
17883
+ verticalChevronList: "chevron",
17884
+ basicFunnel: "funnel",
17885
+ basicCycle: "cycle",
17886
+ basicPie: "cycle",
17887
+ basicRadial: "cycle",
17888
+ basicVenn: "relationship",
17889
+ convergingRadial: "cycle",
17890
+ linearVenn: "relationship",
17891
+ basicTarget: "target",
17892
+ interlockingGears: "gear",
17893
+ hierarchy: "hierarchy"
17894
+ };
17895
+ function resolveEffectiveLayoutType(data) {
17896
+ if (data.resolvedLayoutType && data.resolvedLayoutType !== "unknown") {
17897
+ return data.resolvedLayoutType;
17898
+ }
17899
+ if (data.layout && LAYOUT_PRESET_TO_TYPE[data.layout]) {
17900
+ return LAYOUT_PRESET_TO_TYPE[data.layout];
17901
+ }
17902
+ return resolveLayoutFromRawType(data.layoutType);
17903
+ }
17854
17904
  function buildEffectiveThemeMap(themeColorMap, colorTransformFills) {
17855
17905
  if (!colorTransformFills || colorTransformFills.length === 0) {
17856
17906
  return themeColorMap;
@@ -17952,6 +18002,9 @@ function resolveLayoutFromRawType(layoutType) {
17952
18002
  }
17953
18003
 
17954
18004
  // src/core/utils/smartart-editing-node-ops.ts
18005
+ function markShapesStale(shapes) {
18006
+ return shapes !== void 0 && shapes.length > 0 ? [] : void 0;
18007
+ }
17955
18008
  function nextModelId() {
17956
18009
  return `{${generateFontGuid()}}`;
17957
18010
  }
@@ -18005,8 +18058,7 @@ function addSmartArtNode(data, text, afterNodeId) {
18005
18058
  ...data,
18006
18059
  nodes,
18007
18060
  connections: connections.length > 0 ? connections : void 0,
18008
- // Clear pre-computed shapes to force layout reflow
18009
- drawingShapes: void 0
18061
+ drawingShapes: markShapesStale(data.drawingShapes)
18010
18062
  };
18011
18063
  }
18012
18064
  function removeSmartArtNode(data, nodeId) {
@@ -18035,7 +18087,7 @@ function removeSmartArtNode(data, nodeId) {
18035
18087
  ...data,
18036
18088
  nodes,
18037
18089
  connections: connections.length > 0 ? connections : void 0,
18038
- drawingShapes: void 0
18090
+ drawingShapes: markShapesStale(data.drawingShapes)
18039
18091
  };
18040
18092
  }
18041
18093
  function updateSmartArtNodeText(data, nodeId, newText) {
@@ -18043,9 +18095,48 @@ function updateSmartArtNodeText(data, nodeId, newText) {
18043
18095
  return {
18044
18096
  ...data,
18045
18097
  nodes,
18046
- drawingShapes: void 0
18098
+ drawingShapes: patchDrawingShapeText(data.drawingShapes, data.nodes, nodeId, newText)
18047
18099
  };
18048
18100
  }
18101
+ function patchDrawingShapeText(shapes, nodes, nodeId, newText) {
18102
+ if (!shapes || shapes.length === 0) {
18103
+ return shapes;
18104
+ }
18105
+ const idx = findDrawingShapeIndex(shapes, nodes, nodeId);
18106
+ if (idx < 0) {
18107
+ return [];
18108
+ }
18109
+ const updated = [...shapes];
18110
+ updated[idx] = { ...updated[idx], text: newText };
18111
+ return updated;
18112
+ }
18113
+ function findDrawingShapeIndex(shapes, nodes, nodeId) {
18114
+ for (let i = 0; i < shapes.length; i++) {
18115
+ if (shapes[i].id.startsWith("reflow-") && shapes[i].id.endsWith(`-${nodeId}`)) {
18116
+ return i;
18117
+ }
18118
+ }
18119
+ if (shapes.length === nodes.length) {
18120
+ const nodeIdx = nodes.findIndex((n) => n.id === nodeId);
18121
+ if (nodeIdx >= 0) {
18122
+ return nodeIdx;
18123
+ }
18124
+ }
18125
+ const node = nodes.find((n) => n.id === nodeId);
18126
+ if (node?.text) {
18127
+ const text = node.text.trim();
18128
+ const matches = [];
18129
+ for (let i = 0; i < shapes.length; i++) {
18130
+ if (shapes[i].text?.trim() === text) {
18131
+ matches.push(i);
18132
+ }
18133
+ }
18134
+ if (matches.length === 1) {
18135
+ return matches[0];
18136
+ }
18137
+ }
18138
+ return -1;
18139
+ }
18049
18140
  function reorderSmartArtNode(data, nodeId, direction) {
18050
18141
  const node = data.nodes.find((n) => n.id === nodeId);
18051
18142
  if (!node) {
@@ -18068,7 +18159,7 @@ function reorderSmartArtNode(data, nodeId, direction) {
18068
18159
  return {
18069
18160
  ...data,
18070
18161
  nodes,
18071
- drawingShapes: void 0
18162
+ drawingShapes: markShapesStale(data.drawingShapes)
18072
18163
  };
18073
18164
  }
18074
18165
  function promoteSmartArtNode(data, nodeId) {
@@ -18095,7 +18186,7 @@ function promoteSmartArtNode(data, nodeId) {
18095
18186
  ...data,
18096
18187
  nodes,
18097
18188
  connections: connections.length > 0 ? connections : void 0,
18098
- drawingShapes: void 0
18189
+ drawingShapes: markShapesStale(data.drawingShapes)
18099
18190
  };
18100
18191
  }
18101
18192
  function demoteSmartArtNode(data, nodeId) {
@@ -18122,7 +18213,7 @@ function demoteSmartArtNode(data, nodeId) {
18122
18213
  ...data,
18123
18214
  nodes,
18124
18215
  connections: connections.length > 0 ? connections : void 0,
18125
- drawingShapes: void 0
18216
+ drawingShapes: markShapesStale(data.drawingShapes)
18126
18217
  };
18127
18218
  }
18128
18219
  function addSmartArtNodeAsChild(data, parentId, text) {
@@ -18150,7 +18241,7 @@ function addSmartArtNodeAsChild(data, parentId, text) {
18150
18241
  ...data,
18151
18242
  nodes,
18152
18243
  connections: connections.length > 0 ? connections : void 0,
18153
- drawingShapes: void 0
18244
+ drawingShapes: markShapesStale(data.drawingShapes)
18154
18245
  };
18155
18246
  }
18156
18247
  function reorderSmartArtNodeToIndex(data, nodeId, newIndex) {
@@ -18183,7 +18274,7 @@ function reorderSmartArtNodeToIndex(data, nodeId, newIndex) {
18183
18274
  return {
18184
18275
  ...data,
18185
18276
  nodes,
18186
- drawingShapes: void 0
18277
+ drawingShapes: markShapesStale(data.drawingShapes)
18187
18278
  };
18188
18279
  }
18189
18280
 
@@ -18223,9 +18314,61 @@ function setSmartArtNodeStyle(data, nodeId, style) {
18223
18314
  return {
18224
18315
  ...data,
18225
18316
  nodes,
18226
- drawingShapes: void 0
18317
+ drawingShapes: patchDrawingShapeStyle(data.drawingShapes, data.nodes, nodeId, style)
18227
18318
  };
18228
18319
  }
18320
+ function patchDrawingShapeStyle(shapes, nodes, nodeId, style) {
18321
+ if (!shapes || shapes.length === 0) {
18322
+ return shapes;
18323
+ }
18324
+ const idx = findShapeIndexForNode(shapes, nodes, nodeId);
18325
+ if (idx < 0) {
18326
+ return [];
18327
+ }
18328
+ const patch = {};
18329
+ if (style.fillColor !== void 0) {
18330
+ patch.fillColor = style.fillColor;
18331
+ }
18332
+ if (style.lineColor !== void 0) {
18333
+ patch.strokeColor = style.lineColor;
18334
+ }
18335
+ if (style.fontColor !== void 0) {
18336
+ patch.fontColor = style.fontColor;
18337
+ }
18338
+ if (Object.keys(patch).length === 0) {
18339
+ return shapes;
18340
+ }
18341
+ const updated = [...shapes];
18342
+ updated[idx] = { ...updated[idx], ...patch };
18343
+ return updated;
18344
+ }
18345
+ function findShapeIndexForNode(shapes, nodes, nodeId) {
18346
+ for (let i = 0; i < shapes.length; i++) {
18347
+ if (shapes[i].id.startsWith("reflow-") && shapes[i].id.endsWith(`-${nodeId}`)) {
18348
+ return i;
18349
+ }
18350
+ }
18351
+ if (shapes.length === nodes.length) {
18352
+ const nodeIdx = nodes.findIndex((n) => n.id === nodeId);
18353
+ if (nodeIdx >= 0) {
18354
+ return nodeIdx;
18355
+ }
18356
+ }
18357
+ const node = nodes.find((n) => n.id === nodeId);
18358
+ if (node?.text) {
18359
+ const text = node.text.trim();
18360
+ const matches = [];
18361
+ for (let i = 0; i < shapes.length; i++) {
18362
+ if (shapes[i].text?.trim() === text) {
18363
+ matches.push(i);
18364
+ }
18365
+ }
18366
+ if (matches.length === 1) {
18367
+ return matches[0];
18368
+ }
18369
+ }
18370
+ return -1;
18371
+ }
18229
18372
 
18230
18373
  // src/core/utils/smartart-editing-reflow-layouts-geometric.ts
18231
18374
  function reflowCycle(nodes, bounds) {
@@ -20282,20 +20425,24 @@ function switchSmartArtLayout(currentData, newLayoutType) {
20282
20425
  return {
20283
20426
  ...currentData,
20284
20427
  // Clear the raw layoutType string since the user is explicitly
20285
- // choosing a resolved category this avoids the heuristic
20428
+ // choosing a resolved category - this avoids the heuristic
20286
20429
  // re-resolve overriding their choice.
20287
20430
  layoutType: newLayoutType,
20288
20431
  resolvedLayoutType: newLayoutType,
20289
- // Clear the named layout preset switching category invalidates it
20432
+ // Clear the named layout preset - switching category invalidates it
20290
20433
  layout: void 0,
20291
- // Clear stale pre-computed drawing shapes from the old layout so the
20434
+ // Mark stale pre-computed drawing shapes from the old layout so the
20292
20435
  // reflow pipeline (rebuildDrawingShapesIfCleared) regenerates them for
20293
- // the new layout type; otherwise the renderer keeps preferring the old
20294
- // shapes and the switch has no visible effect.
20295
- drawingShapes: void 0
20436
+ // the new layout type. If the element never had drawing shapes (freshly
20437
+ // inserted), leave undefined so the family SVG renderer handles it
20438
+ // without triggering a lossy polygon-to-chevron reflow.
20439
+ drawingShapes: markShapesStale2(currentData.drawingShapes)
20296
20440
  // Preserve everything else: nodes, connections, colours, styles, chrome, etc.
20297
20441
  };
20298
20442
  }
20443
+ function markShapesStale2(shapes) {
20444
+ return shapes !== void 0 && shapes.length > 0 ? [] : void 0;
20445
+ }
20299
20446
  function isSwitchableLayoutType(layoutType) {
20300
20447
  return SWITCHABLE_LAYOUT_TYPES.includes(layoutType);
20301
20448
  }
@@ -35994,14 +36141,19 @@ function transPointXml(guid, type, cxnGuid) {
35994
36141
  function docPointXml(docGuid, ids) {
35995
36142
  return `<dgm:pt modelId="${docGuid}" type="doc"><dgm:prSet loTypeId="${xmlEscape(ids.layoutUniqueId)}" loCatId="${xmlEscape(ids.layoutCategory)}" qsTypeId="${xmlEscape(ids.quickStyleUniqueId)}" qsCatId="simple" csTypeId="${xmlEscape(ids.colorsUniqueId)}" csCatId="${xmlEscape(ids.colorsCategory)}"/><dgm:spPr/>${EMPTY_TEXT_BODY}</dgm:pt>`;
35996
36143
  }
35997
- function buildFabricatedDiagramDataXml(data, ids) {
35998
- const docGuid = newSmartArtGuid();
36144
+ function buildNodeGuidMap(nodes) {
35999
36145
  const guidByNodeId = /* @__PURE__ */ new Map();
36000
- for (const node of data.nodes) {
36146
+ for (const node of nodes) {
36001
36147
  if (node.id && !guidByNodeId.has(node.id)) {
36002
36148
  guidByNodeId.set(node.id, GUID_MODEL_ID.test(node.id) ? node.id : newSmartArtGuid());
36003
36149
  }
36004
36150
  }
36151
+ return guidByNodeId;
36152
+ }
36153
+ var DSP_DATA_MODEL_EXT_URI = "http://schemas.microsoft.com/office/drawing/2008/diagram";
36154
+ function buildFabricatedDiagramDataXml(data, ids, options = {}) {
36155
+ const docGuid = newSmartArtGuid();
36156
+ const guidByNodeId = options.guidByNodeId ?? buildNodeGuidMap(data.nodes);
36005
36157
  const points2 = [docPointXml(docGuid, ids)];
36006
36158
  const connections = [];
36007
36159
  const nextSrcOrd = /* @__PURE__ */ new Map();
@@ -36024,7 +36176,121 @@ function buildFabricatedDiagramDataXml(data, ids) {
36024
36176
  );
36025
36177
  }
36026
36178
  return `${XML_PROLOG}\r
36027
- <dgm:dataModel ${DGM_XMLNS}><dgm:ptLst>${points2.join("")}</dgm:ptLst><dgm:cxnLst>${connections.join("")}</dgm:cxnLst><dgm:bg/><dgm:whole/></dgm:dataModel>`;
36179
+ <dgm:dataModel ${DGM_XMLNS}><dgm:ptLst>${points2.join("")}</dgm:ptLst><dgm:cxnLst>${connections.join("")}</dgm:cxnLst><dgm:bg/><dgm:whole/>${dataModelExtXml(options.drawingRelId)}</dgm:dataModel>`;
36180
+ }
36181
+ function dataModelExtXml(drawingRelId) {
36182
+ if (!drawingRelId) {
36183
+ return "";
36184
+ }
36185
+ return `<dgm:extLst><a:ext uri="${DSP_DATA_MODEL_EXT_URI}"><dsp:dataModelExt xmlns:dsp="${DSP_DATA_MODEL_EXT_URI}" relId="${xmlEscape(drawingRelId)}" minVer="http://schemas.openxmlformats.org/drawingml/2006/diagram"/></a:ext></dgm:extLst>`;
36186
+ }
36187
+
36188
+ // src/core/core/runtime/smartart-fabrication-drawing.ts
36189
+ var DIAGRAM_DRAWING_CONTENT_TYPE = "application/vnd.ms-office.drawingml.diagramDrawing+xml";
36190
+ var DIAGRAM_DRAWING_REL_TYPE = "http://schemas.microsoft.com/office/2007/relationships/diagramDrawing";
36191
+ var DSP_XMLNS = 'xmlns:dsp="http://schemas.microsoft.com/office/drawing/2008/diagram" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"';
36192
+ var ACCENTS = ["accent1", "accent2", "accent3", "accent4", "accent5", "accent6"];
36193
+ function toEmu(px2) {
36194
+ return Math.round(px2 * EMU_PER_PX);
36195
+ }
36196
+ function normalizeHex3(color) {
36197
+ if (!color) {
36198
+ return void 0;
36199
+ }
36200
+ const hex8 = color.replace(/^#/u, "").trim();
36201
+ return /^[0-9A-Fa-f]{6}$/u.test(hex8) ? hex8.toUpperCase() : void 0;
36202
+ }
36203
+ function resolveShapeModelId(shape, index, nodes, guidByNodeId) {
36204
+ const direct = guidByNodeId.get(shape.id);
36205
+ if (direct) {
36206
+ return direct;
36207
+ }
36208
+ const matched = nodes.find(
36209
+ (node) => node.id && (shape.id === node.id || shape.id.endsWith(`-${node.id}`))
36210
+ );
36211
+ if (matched?.id) {
36212
+ const guid = guidByNodeId.get(matched.id);
36213
+ if (guid) {
36214
+ return guid;
36215
+ }
36216
+ }
36217
+ const positional = nodes[index]?.id;
36218
+ if (positional) {
36219
+ const guid = guidByNodeId.get(positional);
36220
+ if (guid) {
36221
+ return guid;
36222
+ }
36223
+ }
36224
+ return newSmartArtGuid();
36225
+ }
36226
+ function textBodyXml(shape) {
36227
+ const text = shape.text ?? "";
36228
+ const fontColor = normalizeHex3(shape.fontColor);
36229
+ const size = shape.fontSize && shape.fontSize > 0 ? ` sz="${Math.round(shape.fontSize * 100)}"` : "";
36230
+ const fill = fontColor ? `<a:solidFill><a:srgbClr val="${fontColor}"/></a:solidFill>` : "";
36231
+ const rPr = `<a:rPr lang="en-US"${size}>${fill}</a:rPr>`;
36232
+ const run = text ? `<a:r>${rPr}<a:t>${xmlEscape(text)}</a:t></a:r>` : `<a:endParaRPr lang="en-US"/>`;
36233
+ return `<dsp:txBody><a:bodyPr/><a:lstStyle/><a:p><a:pPr algn="ctr"/>${run}</a:p></dsp:txBody>`;
36234
+ }
36235
+ function styleXml(index) {
36236
+ const accent = ACCENTS[index % ACCENTS.length];
36237
+ return `<dsp:style><a:lnRef idx="2"><a:schemeClr val="${accent}"><a:shade val="50000"/></a:schemeClr></a:lnRef><a:fillRef idx="1"><a:schemeClr val="${accent}"/></a:fillRef><a:effectRef idx="0"><a:schemeClr val="${accent}"/></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="lt1"/></a:fontRef></dsp:style>`;
36238
+ }
36239
+ function shapePropsXml(shape) {
36240
+ const rot = shape.rotation ? ` rot="${Math.round(shape.rotation * 6e4)}"` : "";
36241
+ const xfrm = `<a:xfrm${rot}><a:off x="${toEmu(shape.x)}" y="${toEmu(shape.y)}"/><a:ext cx="${toEmu(Math.max(shape.width, 1))}" cy="${toEmu(Math.max(shape.height, 1))}"/></a:xfrm>`;
36242
+ const prst = shape.shapeType && shape.shapeType.length > 0 ? shape.shapeType : "rect";
36243
+ const geom = `<a:prstGeom prst="${xmlEscape(prst)}"><a:avLst/></a:prstGeom>`;
36244
+ const fillHex = normalizeHex3(shape.fillColor);
36245
+ const fill = fillHex ? `<a:solidFill><a:srgbClr val="${fillHex}"/></a:solidFill>` : "";
36246
+ const strokeHex = normalizeHex3(shape.strokeColor);
36247
+ const strokeW = shape.strokeWidth && shape.strokeWidth > 0 ? ` w="${Math.round(shape.strokeWidth * 12700)}"` : "";
36248
+ const ln = strokeHex ? `<a:ln${strokeW}><a:solidFill><a:srgbClr val="${strokeHex}"/></a:solidFill></a:ln>` : "";
36249
+ return `<dsp:spPr>${xfrm}${geom}${fill}${ln}</dsp:spPr>`;
36250
+ }
36251
+ function shapeXml(shape, index, nodes, guidByNodeId) {
36252
+ const modelId = resolveShapeModelId(shape, index, nodes, guidByNodeId);
36253
+ return `<dsp:sp modelId="${modelId}"><dsp:nvSpPr><dsp:cNvPr id="0" name=""/><dsp:cNvSpPr/></dsp:nvSpPr>${shapePropsXml(shape)}${styleXml(index)}${textBodyXml(shape)}</dsp:sp>`;
36254
+ }
36255
+ function buildFabricatedDrawingXml(shapes, nodes, guidByNodeId) {
36256
+ if (!shapes || shapes.length === 0) {
36257
+ return void 0;
36258
+ }
36259
+ const body = shapes.map((shape, index) => shapeXml(shape, index, nodes, guidByNodeId)).join("");
36260
+ return `${XML_PROLOG}\r
36261
+ <dsp:drawing ${DSP_XMLNS}><dsp:spTree><dsp:nvGrpSpPr><dsp:cNvPr id="0" name=""/><dsp:cNvGrpSpPr/></dsp:nvGrpSpPr><dsp:grpSpPr/>${body}</dsp:spTree></dsp:drawing>`;
36262
+ }
36263
+ function buildDiagramDataRelsXml(drawingRelId, drawingFileName) {
36264
+ return `${XML_PROLOG}\r
36265
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="${xmlEscape(drawingRelId)}" Type="${DIAGRAM_DRAWING_REL_TYPE}" Target="${xmlEscape(drawingFileName)}"/></Relationships>`;
36266
+ }
36267
+ function smartArtElementsToDrawingShapes(elements) {
36268
+ if (!elements || elements.length === 0) {
36269
+ return [];
36270
+ }
36271
+ const shapes = [];
36272
+ for (const el of elements) {
36273
+ if (el.type !== "shape") {
36274
+ continue;
36275
+ }
36276
+ const shape = el;
36277
+ shapes.push({
36278
+ id: shape.id,
36279
+ shapeType: shape.shapeType ?? "rect",
36280
+ x: shape.x,
36281
+ y: shape.y,
36282
+ width: shape.width,
36283
+ height: shape.height,
36284
+ rotation: shape.rotation,
36285
+ fillColor: shape.shapeStyle?.fillColor,
36286
+ strokeColor: shape.shapeStyle?.strokeColor,
36287
+ strokeWidth: shape.shapeStyle?.strokeWidth,
36288
+ text: shape.text,
36289
+ fontSize: shape.textStyle?.fontSize,
36290
+ fontColor: shape.textStyle?.color
36291
+ });
36292
+ }
36293
+ return shapes;
36028
36294
  }
36029
36295
 
36030
36296
  // src/core/core/runtime/smartart-fabrication-hierarchy.ts
@@ -36232,14 +36498,29 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
36232
36498
  const data = el.smartArtData;
36233
36499
  const family = resolveFabricatedLayoutFamily(data);
36234
36500
  const index = this.nextDiagramPartIndex();
36501
+ const guidByNodeId = buildNodeGuidMap(data.nodes);
36502
+ const drawingShapes = data.drawingShapes && data.drawingShapes.length > 0 ? data.drawingShapes : smartArtElementsToDrawingShapes(
36503
+ decomposeSmartArt(data, {
36504
+ x: 0,
36505
+ y: 0,
36506
+ width: Math.max(el.width, 1),
36507
+ height: Math.max(el.height, 1)
36508
+ })
36509
+ );
36510
+ const drawingRelId = "rId1";
36511
+ const drawingXml = buildFabricatedDrawingXml(drawingShapes, data.nodes, guidByNodeId);
36235
36512
  const payloads = {
36236
- data: buildFabricatedDiagramDataXml(data, {
36237
- layoutUniqueId: fabricatedLayoutUniqueId(family),
36238
- layoutCategory: fabricatedLayoutCategory(family),
36239
- quickStyleUniqueId: FABRICATED_QUICKSTYLE_UNIQUE_ID,
36240
- colorsUniqueId: fabricatedColorsUniqueId(data.colorScheme),
36241
- colorsCategory: fabricatedColorsCategory(data.colorScheme)
36242
- }),
36513
+ data: buildFabricatedDiagramDataXml(
36514
+ data,
36515
+ {
36516
+ layoutUniqueId: fabricatedLayoutUniqueId(family),
36517
+ layoutCategory: fabricatedLayoutCategory(family),
36518
+ quickStyleUniqueId: FABRICATED_QUICKSTYLE_UNIQUE_ID,
36519
+ colorsUniqueId: fabricatedColorsUniqueId(data.colorScheme),
36520
+ colorsCategory: fabricatedColorsCategory(data.colorScheme)
36521
+ },
36522
+ { guidByNodeId, drawingRelId: drawingXml ? drawingRelId : void 0 }
36523
+ ),
36243
36524
  layout: buildFabricatedLayoutDefXml(family),
36244
36525
  quickStyle: buildFabricatedQuickStyleXml(),
36245
36526
  colors: buildFabricatedColorsXml(data.colorScheme)
@@ -36263,6 +36544,19 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
36263
36544
  });
36264
36545
  relIds[kind.relAttr] = relId;
36265
36546
  }
36547
+ if (drawingXml) {
36548
+ const drawingFile = `drawing${index}.xml`;
36549
+ const drawingPath = `ppt/diagrams/${drawingFile}`;
36550
+ this.zip.file(drawingPath, drawingXml);
36551
+ (this.pendingDiagramContentTypes ??= []).push({
36552
+ partName: `/${drawingPath}`,
36553
+ contentType: DIAGRAM_DRAWING_CONTENT_TYPE
36554
+ });
36555
+ this.zip.file(
36556
+ `ppt/diagrams/_rels/data${index}.xml.rels`,
36557
+ buildDiagramDataRelsXml(drawingRelId, drawingFile)
36558
+ );
36559
+ }
36266
36560
  const EMU = _PptxHandlerRuntime.EMU_PER_PX;
36267
36561
  return {
36268
36562
  "p:nvGraphicFramePr": {
@@ -37618,6 +37912,9 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
37618
37912
  slideLayoutRelationshipType,
37619
37913
  relationshipsNamespace
37620
37914
  });
37915
+ for (const slide of slides) {
37916
+ await this.processSlideForSave(slide, saveSession, saveConstants);
37917
+ }
37621
37918
  const contentTypesXml2 = await this.zip.file("[Content_Types].xml")?.async("string");
37622
37919
  if (contentTypesXml2) {
37623
37920
  const contentTypesData = this.parser.parse(contentTypesXml2);
@@ -37629,9 +37926,6 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
37629
37926
  });
37630
37927
  this.zip.file("[Content_Types].xml", this.builder.build(contentTypesData));
37631
37928
  }
37632
- for (const slide of slides) {
37633
- await this.processSlideForSave(slide, saveSession, saveConstants);
37634
- }
37635
37929
  for (const existingCommentPath of saveSession.getExistingCommentPaths()) {
37636
37930
  if (saveSession.isCommentPathActive(existingCommentPath)) {
37637
37931
  continue;
package/dist/index.mjs CHANGED
@@ -16902,6 +16902,7 @@ function getDefaultShapeType(layoutType) {
16902
16902
  case "cycle":
16903
16903
  case "target":
16904
16904
  case "gear":
16905
+ return "ellipse";
16905
16906
  case "relationship":
16906
16907
  case "venn":
16907
16908
  return "ellipse";
@@ -16909,7 +16910,10 @@ function getDefaultShapeType(layoutType) {
16909
16910
  return "chevron";
16910
16911
  case "pyramid":
16911
16912
  case "funnel":
16912
- return "rect";
16913
+ return "trapezoid";
16914
+ case "timeline":
16915
+ case "bending":
16916
+ return "roundRect";
16913
16917
  default:
16914
16918
  return "roundRect";
16915
16919
  }
@@ -16999,7 +17003,7 @@ function layoutCycle(nodes, bounds, themeColorMap) {
16999
17003
  const ny = cy + radius * Math.sin(angle) - nodeH / 2;
17000
17004
  const fill = accentColor(i, themeColorMap);
17001
17005
  elements.push(
17002
- makeShapeElement(nextId("sa-cycle"), nx, ny, nodeW, nodeH, "roundRect", fill, node.text, {
17006
+ makeShapeElement(nextId("sa-cycle"), nx, ny, nodeW, nodeH, "ellipse", fill, node.text, {
17003
17007
  fontSize: Math.max(7, Math.min(10, nodeW * 0.1))
17004
17008
  })
17005
17009
  );
@@ -17066,7 +17070,7 @@ function layoutPyramid(nodes, bounds, themeColorMap) {
17066
17070
  const x = bounds.x + (bounds.width - w) / 2;
17067
17071
  const y = bounds.y + padding + i * (bandH + gap);
17068
17072
  const fill = accentColor(i, themeColorMap);
17069
- return makeShapeElement(nextId("sa-pyramid"), x, y, w, bandH, "rect", fill, node.text, {
17073
+ return makeShapeElement(nextId("sa-pyramid"), x, y, w, bandH, "trapezoid", fill, node.text, {
17070
17074
  fontSize: Math.max(8, Math.min(11, bandH * 0.4))
17071
17075
  });
17072
17076
  });
@@ -17815,7 +17819,7 @@ function decomposeSmartArt(smartArtData, containerBounds, themeColorMap, layoutD
17815
17819
  themeColorMap,
17816
17820
  smartArtData.colorTransform?.fillColors
17817
17821
  );
17818
- const layoutType = smartArtData.resolvedLayoutType ?? resolveLayoutFromRawType(smartArtData.layoutType);
17822
+ const layoutType = resolveEffectiveLayoutType(smartArtData);
17819
17823
  const namedLayout = smartArtData.layout;
17820
17824
  if (namedLayout) {
17821
17825
  const namedResult = dispatchNamedLayout(namedLayout, nodes, containerBounds, effectiveThemeMap);
@@ -17846,6 +17850,52 @@ function dispatchLayoutByType(layoutType, nodes, containerBounds, effectiveTheme
17846
17850
  return layoutWithHeuristic(nodes, containerBounds, effectiveThemeMap);
17847
17851
  }
17848
17852
  }
17853
+ var LAYOUT_PRESET_TO_TYPE = {
17854
+ basicBlockList: "list",
17855
+ alternatingHexagons: "list",
17856
+ horizontalBulletList: "list",
17857
+ stackedList: "list",
17858
+ tableList: "list",
17859
+ trapezoidList: "list",
17860
+ pictureAccentList: "list",
17861
+ verticalBlockList: "list",
17862
+ groupedList: "list",
17863
+ pyramidList: "list",
17864
+ horizontalPictureList: "list",
17865
+ basicMatrix: "matrix",
17866
+ basicPyramid: "pyramid",
17867
+ invertedPyramid: "pyramid",
17868
+ basicChevronProcess: "chevron",
17869
+ continuousBlockProcess: "process",
17870
+ segmentedProcess: "process",
17871
+ upwardArrow: "process",
17872
+ basicTimeline: "timeline",
17873
+ bendingProcess: "bending",
17874
+ stepDownProcess: "process",
17875
+ alternatingFlow: "process",
17876
+ descendingProcess: "process",
17877
+ accentProcess: "process",
17878
+ verticalChevronList: "chevron",
17879
+ basicFunnel: "funnel",
17880
+ basicCycle: "cycle",
17881
+ basicPie: "cycle",
17882
+ basicRadial: "cycle",
17883
+ basicVenn: "relationship",
17884
+ convergingRadial: "cycle",
17885
+ linearVenn: "relationship",
17886
+ basicTarget: "target",
17887
+ interlockingGears: "gear",
17888
+ hierarchy: "hierarchy"
17889
+ };
17890
+ function resolveEffectiveLayoutType(data) {
17891
+ if (data.resolvedLayoutType && data.resolvedLayoutType !== "unknown") {
17892
+ return data.resolvedLayoutType;
17893
+ }
17894
+ if (data.layout && LAYOUT_PRESET_TO_TYPE[data.layout]) {
17895
+ return LAYOUT_PRESET_TO_TYPE[data.layout];
17896
+ }
17897
+ return resolveLayoutFromRawType(data.layoutType);
17898
+ }
17849
17899
  function buildEffectiveThemeMap(themeColorMap, colorTransformFills) {
17850
17900
  if (!colorTransformFills || colorTransformFills.length === 0) {
17851
17901
  return themeColorMap;
@@ -17947,6 +17997,9 @@ function resolveLayoutFromRawType(layoutType) {
17947
17997
  }
17948
17998
 
17949
17999
  // src/core/utils/smartart-editing-node-ops.ts
18000
+ function markShapesStale(shapes) {
18001
+ return shapes !== void 0 && shapes.length > 0 ? [] : void 0;
18002
+ }
17950
18003
  function nextModelId() {
17951
18004
  return `{${generateFontGuid()}}`;
17952
18005
  }
@@ -18000,8 +18053,7 @@ function addSmartArtNode(data, text, afterNodeId) {
18000
18053
  ...data,
18001
18054
  nodes,
18002
18055
  connections: connections.length > 0 ? connections : void 0,
18003
- // Clear pre-computed shapes to force layout reflow
18004
- drawingShapes: void 0
18056
+ drawingShapes: markShapesStale(data.drawingShapes)
18005
18057
  };
18006
18058
  }
18007
18059
  function removeSmartArtNode(data, nodeId) {
@@ -18030,7 +18082,7 @@ function removeSmartArtNode(data, nodeId) {
18030
18082
  ...data,
18031
18083
  nodes,
18032
18084
  connections: connections.length > 0 ? connections : void 0,
18033
- drawingShapes: void 0
18085
+ drawingShapes: markShapesStale(data.drawingShapes)
18034
18086
  };
18035
18087
  }
18036
18088
  function updateSmartArtNodeText(data, nodeId, newText) {
@@ -18038,9 +18090,48 @@ function updateSmartArtNodeText(data, nodeId, newText) {
18038
18090
  return {
18039
18091
  ...data,
18040
18092
  nodes,
18041
- drawingShapes: void 0
18093
+ drawingShapes: patchDrawingShapeText(data.drawingShapes, data.nodes, nodeId, newText)
18042
18094
  };
18043
18095
  }
18096
+ function patchDrawingShapeText(shapes, nodes, nodeId, newText) {
18097
+ if (!shapes || shapes.length === 0) {
18098
+ return shapes;
18099
+ }
18100
+ const idx = findDrawingShapeIndex(shapes, nodes, nodeId);
18101
+ if (idx < 0) {
18102
+ return [];
18103
+ }
18104
+ const updated = [...shapes];
18105
+ updated[idx] = { ...updated[idx], text: newText };
18106
+ return updated;
18107
+ }
18108
+ function findDrawingShapeIndex(shapes, nodes, nodeId) {
18109
+ for (let i = 0; i < shapes.length; i++) {
18110
+ if (shapes[i].id.startsWith("reflow-") && shapes[i].id.endsWith(`-${nodeId}`)) {
18111
+ return i;
18112
+ }
18113
+ }
18114
+ if (shapes.length === nodes.length) {
18115
+ const nodeIdx = nodes.findIndex((n) => n.id === nodeId);
18116
+ if (nodeIdx >= 0) {
18117
+ return nodeIdx;
18118
+ }
18119
+ }
18120
+ const node = nodes.find((n) => n.id === nodeId);
18121
+ if (node?.text) {
18122
+ const text = node.text.trim();
18123
+ const matches = [];
18124
+ for (let i = 0; i < shapes.length; i++) {
18125
+ if (shapes[i].text?.trim() === text) {
18126
+ matches.push(i);
18127
+ }
18128
+ }
18129
+ if (matches.length === 1) {
18130
+ return matches[0];
18131
+ }
18132
+ }
18133
+ return -1;
18134
+ }
18044
18135
  function reorderSmartArtNode(data, nodeId, direction) {
18045
18136
  const node = data.nodes.find((n) => n.id === nodeId);
18046
18137
  if (!node) {
@@ -18063,7 +18154,7 @@ function reorderSmartArtNode(data, nodeId, direction) {
18063
18154
  return {
18064
18155
  ...data,
18065
18156
  nodes,
18066
- drawingShapes: void 0
18157
+ drawingShapes: markShapesStale(data.drawingShapes)
18067
18158
  };
18068
18159
  }
18069
18160
  function promoteSmartArtNode(data, nodeId) {
@@ -18090,7 +18181,7 @@ function promoteSmartArtNode(data, nodeId) {
18090
18181
  ...data,
18091
18182
  nodes,
18092
18183
  connections: connections.length > 0 ? connections : void 0,
18093
- drawingShapes: void 0
18184
+ drawingShapes: markShapesStale(data.drawingShapes)
18094
18185
  };
18095
18186
  }
18096
18187
  function demoteSmartArtNode(data, nodeId) {
@@ -18117,7 +18208,7 @@ function demoteSmartArtNode(data, nodeId) {
18117
18208
  ...data,
18118
18209
  nodes,
18119
18210
  connections: connections.length > 0 ? connections : void 0,
18120
- drawingShapes: void 0
18211
+ drawingShapes: markShapesStale(data.drawingShapes)
18121
18212
  };
18122
18213
  }
18123
18214
  function addSmartArtNodeAsChild(data, parentId, text) {
@@ -18145,7 +18236,7 @@ function addSmartArtNodeAsChild(data, parentId, text) {
18145
18236
  ...data,
18146
18237
  nodes,
18147
18238
  connections: connections.length > 0 ? connections : void 0,
18148
- drawingShapes: void 0
18239
+ drawingShapes: markShapesStale(data.drawingShapes)
18149
18240
  };
18150
18241
  }
18151
18242
  function reorderSmartArtNodeToIndex(data, nodeId, newIndex) {
@@ -18178,7 +18269,7 @@ function reorderSmartArtNodeToIndex(data, nodeId, newIndex) {
18178
18269
  return {
18179
18270
  ...data,
18180
18271
  nodes,
18181
- drawingShapes: void 0
18272
+ drawingShapes: markShapesStale(data.drawingShapes)
18182
18273
  };
18183
18274
  }
18184
18275
 
@@ -18218,9 +18309,61 @@ function setSmartArtNodeStyle(data, nodeId, style) {
18218
18309
  return {
18219
18310
  ...data,
18220
18311
  nodes,
18221
- drawingShapes: void 0
18312
+ drawingShapes: patchDrawingShapeStyle(data.drawingShapes, data.nodes, nodeId, style)
18222
18313
  };
18223
18314
  }
18315
+ function patchDrawingShapeStyle(shapes, nodes, nodeId, style) {
18316
+ if (!shapes || shapes.length === 0) {
18317
+ return shapes;
18318
+ }
18319
+ const idx = findShapeIndexForNode(shapes, nodes, nodeId);
18320
+ if (idx < 0) {
18321
+ return [];
18322
+ }
18323
+ const patch = {};
18324
+ if (style.fillColor !== void 0) {
18325
+ patch.fillColor = style.fillColor;
18326
+ }
18327
+ if (style.lineColor !== void 0) {
18328
+ patch.strokeColor = style.lineColor;
18329
+ }
18330
+ if (style.fontColor !== void 0) {
18331
+ patch.fontColor = style.fontColor;
18332
+ }
18333
+ if (Object.keys(patch).length === 0) {
18334
+ return shapes;
18335
+ }
18336
+ const updated = [...shapes];
18337
+ updated[idx] = { ...updated[idx], ...patch };
18338
+ return updated;
18339
+ }
18340
+ function findShapeIndexForNode(shapes, nodes, nodeId) {
18341
+ for (let i = 0; i < shapes.length; i++) {
18342
+ if (shapes[i].id.startsWith("reflow-") && shapes[i].id.endsWith(`-${nodeId}`)) {
18343
+ return i;
18344
+ }
18345
+ }
18346
+ if (shapes.length === nodes.length) {
18347
+ const nodeIdx = nodes.findIndex((n) => n.id === nodeId);
18348
+ if (nodeIdx >= 0) {
18349
+ return nodeIdx;
18350
+ }
18351
+ }
18352
+ const node = nodes.find((n) => n.id === nodeId);
18353
+ if (node?.text) {
18354
+ const text = node.text.trim();
18355
+ const matches = [];
18356
+ for (let i = 0; i < shapes.length; i++) {
18357
+ if (shapes[i].text?.trim() === text) {
18358
+ matches.push(i);
18359
+ }
18360
+ }
18361
+ if (matches.length === 1) {
18362
+ return matches[0];
18363
+ }
18364
+ }
18365
+ return -1;
18366
+ }
18224
18367
 
18225
18368
  // src/core/utils/smartart-editing-reflow-layouts-geometric.ts
18226
18369
  function reflowCycle(nodes, bounds) {
@@ -20277,20 +20420,24 @@ function switchSmartArtLayout(currentData, newLayoutType) {
20277
20420
  return {
20278
20421
  ...currentData,
20279
20422
  // Clear the raw layoutType string since the user is explicitly
20280
- // choosing a resolved category this avoids the heuristic
20423
+ // choosing a resolved category - this avoids the heuristic
20281
20424
  // re-resolve overriding their choice.
20282
20425
  layoutType: newLayoutType,
20283
20426
  resolvedLayoutType: newLayoutType,
20284
- // Clear the named layout preset switching category invalidates it
20427
+ // Clear the named layout preset - switching category invalidates it
20285
20428
  layout: void 0,
20286
- // Clear stale pre-computed drawing shapes from the old layout so the
20429
+ // Mark stale pre-computed drawing shapes from the old layout so the
20287
20430
  // reflow pipeline (rebuildDrawingShapesIfCleared) regenerates them for
20288
- // the new layout type; otherwise the renderer keeps preferring the old
20289
- // shapes and the switch has no visible effect.
20290
- drawingShapes: void 0
20431
+ // the new layout type. If the element never had drawing shapes (freshly
20432
+ // inserted), leave undefined so the family SVG renderer handles it
20433
+ // without triggering a lossy polygon-to-chevron reflow.
20434
+ drawingShapes: markShapesStale2(currentData.drawingShapes)
20291
20435
  // Preserve everything else: nodes, connections, colours, styles, chrome, etc.
20292
20436
  };
20293
20437
  }
20438
+ function markShapesStale2(shapes) {
20439
+ return shapes !== void 0 && shapes.length > 0 ? [] : void 0;
20440
+ }
20294
20441
  function isSwitchableLayoutType(layoutType) {
20295
20442
  return SWITCHABLE_LAYOUT_TYPES.includes(layoutType);
20296
20443
  }
@@ -35989,14 +36136,19 @@ function transPointXml(guid, type, cxnGuid) {
35989
36136
  function docPointXml(docGuid, ids) {
35990
36137
  return `<dgm:pt modelId="${docGuid}" type="doc"><dgm:prSet loTypeId="${xmlEscape(ids.layoutUniqueId)}" loCatId="${xmlEscape(ids.layoutCategory)}" qsTypeId="${xmlEscape(ids.quickStyleUniqueId)}" qsCatId="simple" csTypeId="${xmlEscape(ids.colorsUniqueId)}" csCatId="${xmlEscape(ids.colorsCategory)}"/><dgm:spPr/>${EMPTY_TEXT_BODY}</dgm:pt>`;
35991
36138
  }
35992
- function buildFabricatedDiagramDataXml(data, ids) {
35993
- const docGuid = newSmartArtGuid();
36139
+ function buildNodeGuidMap(nodes) {
35994
36140
  const guidByNodeId = /* @__PURE__ */ new Map();
35995
- for (const node of data.nodes) {
36141
+ for (const node of nodes) {
35996
36142
  if (node.id && !guidByNodeId.has(node.id)) {
35997
36143
  guidByNodeId.set(node.id, GUID_MODEL_ID.test(node.id) ? node.id : newSmartArtGuid());
35998
36144
  }
35999
36145
  }
36146
+ return guidByNodeId;
36147
+ }
36148
+ var DSP_DATA_MODEL_EXT_URI = "http://schemas.microsoft.com/office/drawing/2008/diagram";
36149
+ function buildFabricatedDiagramDataXml(data, ids, options = {}) {
36150
+ const docGuid = newSmartArtGuid();
36151
+ const guidByNodeId = options.guidByNodeId ?? buildNodeGuidMap(data.nodes);
36000
36152
  const points2 = [docPointXml(docGuid, ids)];
36001
36153
  const connections = [];
36002
36154
  const nextSrcOrd = /* @__PURE__ */ new Map();
@@ -36019,7 +36171,121 @@ function buildFabricatedDiagramDataXml(data, ids) {
36019
36171
  );
36020
36172
  }
36021
36173
  return `${XML_PROLOG}\r
36022
- <dgm:dataModel ${DGM_XMLNS}><dgm:ptLst>${points2.join("")}</dgm:ptLst><dgm:cxnLst>${connections.join("")}</dgm:cxnLst><dgm:bg/><dgm:whole/></dgm:dataModel>`;
36174
+ <dgm:dataModel ${DGM_XMLNS}><dgm:ptLst>${points2.join("")}</dgm:ptLst><dgm:cxnLst>${connections.join("")}</dgm:cxnLst><dgm:bg/><dgm:whole/>${dataModelExtXml(options.drawingRelId)}</dgm:dataModel>`;
36175
+ }
36176
+ function dataModelExtXml(drawingRelId) {
36177
+ if (!drawingRelId) {
36178
+ return "";
36179
+ }
36180
+ return `<dgm:extLst><a:ext uri="${DSP_DATA_MODEL_EXT_URI}"><dsp:dataModelExt xmlns:dsp="${DSP_DATA_MODEL_EXT_URI}" relId="${xmlEscape(drawingRelId)}" minVer="http://schemas.openxmlformats.org/drawingml/2006/diagram"/></a:ext></dgm:extLst>`;
36181
+ }
36182
+
36183
+ // src/core/core/runtime/smartart-fabrication-drawing.ts
36184
+ var DIAGRAM_DRAWING_CONTENT_TYPE = "application/vnd.ms-office.drawingml.diagramDrawing+xml";
36185
+ var DIAGRAM_DRAWING_REL_TYPE = "http://schemas.microsoft.com/office/2007/relationships/diagramDrawing";
36186
+ var DSP_XMLNS = 'xmlns:dsp="http://schemas.microsoft.com/office/drawing/2008/diagram" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"';
36187
+ var ACCENTS = ["accent1", "accent2", "accent3", "accent4", "accent5", "accent6"];
36188
+ function toEmu(px2) {
36189
+ return Math.round(px2 * EMU_PER_PX);
36190
+ }
36191
+ function normalizeHex3(color) {
36192
+ if (!color) {
36193
+ return void 0;
36194
+ }
36195
+ const hex8 = color.replace(/^#/u, "").trim();
36196
+ return /^[0-9A-Fa-f]{6}$/u.test(hex8) ? hex8.toUpperCase() : void 0;
36197
+ }
36198
+ function resolveShapeModelId(shape, index, nodes, guidByNodeId) {
36199
+ const direct = guidByNodeId.get(shape.id);
36200
+ if (direct) {
36201
+ return direct;
36202
+ }
36203
+ const matched = nodes.find(
36204
+ (node) => node.id && (shape.id === node.id || shape.id.endsWith(`-${node.id}`))
36205
+ );
36206
+ if (matched?.id) {
36207
+ const guid = guidByNodeId.get(matched.id);
36208
+ if (guid) {
36209
+ return guid;
36210
+ }
36211
+ }
36212
+ const positional = nodes[index]?.id;
36213
+ if (positional) {
36214
+ const guid = guidByNodeId.get(positional);
36215
+ if (guid) {
36216
+ return guid;
36217
+ }
36218
+ }
36219
+ return newSmartArtGuid();
36220
+ }
36221
+ function textBodyXml(shape) {
36222
+ const text = shape.text ?? "";
36223
+ const fontColor = normalizeHex3(shape.fontColor);
36224
+ const size = shape.fontSize && shape.fontSize > 0 ? ` sz="${Math.round(shape.fontSize * 100)}"` : "";
36225
+ const fill = fontColor ? `<a:solidFill><a:srgbClr val="${fontColor}"/></a:solidFill>` : "";
36226
+ const rPr = `<a:rPr lang="en-US"${size}>${fill}</a:rPr>`;
36227
+ const run = text ? `<a:r>${rPr}<a:t>${xmlEscape(text)}</a:t></a:r>` : `<a:endParaRPr lang="en-US"/>`;
36228
+ return `<dsp:txBody><a:bodyPr/><a:lstStyle/><a:p><a:pPr algn="ctr"/>${run}</a:p></dsp:txBody>`;
36229
+ }
36230
+ function styleXml(index) {
36231
+ const accent = ACCENTS[index % ACCENTS.length];
36232
+ return `<dsp:style><a:lnRef idx="2"><a:schemeClr val="${accent}"><a:shade val="50000"/></a:schemeClr></a:lnRef><a:fillRef idx="1"><a:schemeClr val="${accent}"/></a:fillRef><a:effectRef idx="0"><a:schemeClr val="${accent}"/></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="lt1"/></a:fontRef></dsp:style>`;
36233
+ }
36234
+ function shapePropsXml(shape) {
36235
+ const rot = shape.rotation ? ` rot="${Math.round(shape.rotation * 6e4)}"` : "";
36236
+ const xfrm = `<a:xfrm${rot}><a:off x="${toEmu(shape.x)}" y="${toEmu(shape.y)}"/><a:ext cx="${toEmu(Math.max(shape.width, 1))}" cy="${toEmu(Math.max(shape.height, 1))}"/></a:xfrm>`;
36237
+ const prst = shape.shapeType && shape.shapeType.length > 0 ? shape.shapeType : "rect";
36238
+ const geom = `<a:prstGeom prst="${xmlEscape(prst)}"><a:avLst/></a:prstGeom>`;
36239
+ const fillHex = normalizeHex3(shape.fillColor);
36240
+ const fill = fillHex ? `<a:solidFill><a:srgbClr val="${fillHex}"/></a:solidFill>` : "";
36241
+ const strokeHex = normalizeHex3(shape.strokeColor);
36242
+ const strokeW = shape.strokeWidth && shape.strokeWidth > 0 ? ` w="${Math.round(shape.strokeWidth * 12700)}"` : "";
36243
+ const ln = strokeHex ? `<a:ln${strokeW}><a:solidFill><a:srgbClr val="${strokeHex}"/></a:solidFill></a:ln>` : "";
36244
+ return `<dsp:spPr>${xfrm}${geom}${fill}${ln}</dsp:spPr>`;
36245
+ }
36246
+ function shapeXml(shape, index, nodes, guidByNodeId) {
36247
+ const modelId = resolveShapeModelId(shape, index, nodes, guidByNodeId);
36248
+ return `<dsp:sp modelId="${modelId}"><dsp:nvSpPr><dsp:cNvPr id="0" name=""/><dsp:cNvSpPr/></dsp:nvSpPr>${shapePropsXml(shape)}${styleXml(index)}${textBodyXml(shape)}</dsp:sp>`;
36249
+ }
36250
+ function buildFabricatedDrawingXml(shapes, nodes, guidByNodeId) {
36251
+ if (!shapes || shapes.length === 0) {
36252
+ return void 0;
36253
+ }
36254
+ const body = shapes.map((shape, index) => shapeXml(shape, index, nodes, guidByNodeId)).join("");
36255
+ return `${XML_PROLOG}\r
36256
+ <dsp:drawing ${DSP_XMLNS}><dsp:spTree><dsp:nvGrpSpPr><dsp:cNvPr id="0" name=""/><dsp:cNvGrpSpPr/></dsp:nvGrpSpPr><dsp:grpSpPr/>${body}</dsp:spTree></dsp:drawing>`;
36257
+ }
36258
+ function buildDiagramDataRelsXml(drawingRelId, drawingFileName) {
36259
+ return `${XML_PROLOG}\r
36260
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="${xmlEscape(drawingRelId)}" Type="${DIAGRAM_DRAWING_REL_TYPE}" Target="${xmlEscape(drawingFileName)}"/></Relationships>`;
36261
+ }
36262
+ function smartArtElementsToDrawingShapes(elements) {
36263
+ if (!elements || elements.length === 0) {
36264
+ return [];
36265
+ }
36266
+ const shapes = [];
36267
+ for (const el of elements) {
36268
+ if (el.type !== "shape") {
36269
+ continue;
36270
+ }
36271
+ const shape = el;
36272
+ shapes.push({
36273
+ id: shape.id,
36274
+ shapeType: shape.shapeType ?? "rect",
36275
+ x: shape.x,
36276
+ y: shape.y,
36277
+ width: shape.width,
36278
+ height: shape.height,
36279
+ rotation: shape.rotation,
36280
+ fillColor: shape.shapeStyle?.fillColor,
36281
+ strokeColor: shape.shapeStyle?.strokeColor,
36282
+ strokeWidth: shape.shapeStyle?.strokeWidth,
36283
+ text: shape.text,
36284
+ fontSize: shape.textStyle?.fontSize,
36285
+ fontColor: shape.textStyle?.color
36286
+ });
36287
+ }
36288
+ return shapes;
36023
36289
  }
36024
36290
 
36025
36291
  // src/core/core/runtime/smartart-fabrication-hierarchy.ts
@@ -36227,14 +36493,29 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
36227
36493
  const data = el.smartArtData;
36228
36494
  const family = resolveFabricatedLayoutFamily(data);
36229
36495
  const index = this.nextDiagramPartIndex();
36496
+ const guidByNodeId = buildNodeGuidMap(data.nodes);
36497
+ const drawingShapes = data.drawingShapes && data.drawingShapes.length > 0 ? data.drawingShapes : smartArtElementsToDrawingShapes(
36498
+ decomposeSmartArt(data, {
36499
+ x: 0,
36500
+ y: 0,
36501
+ width: Math.max(el.width, 1),
36502
+ height: Math.max(el.height, 1)
36503
+ })
36504
+ );
36505
+ const drawingRelId = "rId1";
36506
+ const drawingXml = buildFabricatedDrawingXml(drawingShapes, data.nodes, guidByNodeId);
36230
36507
  const payloads = {
36231
- data: buildFabricatedDiagramDataXml(data, {
36232
- layoutUniqueId: fabricatedLayoutUniqueId(family),
36233
- layoutCategory: fabricatedLayoutCategory(family),
36234
- quickStyleUniqueId: FABRICATED_QUICKSTYLE_UNIQUE_ID,
36235
- colorsUniqueId: fabricatedColorsUniqueId(data.colorScheme),
36236
- colorsCategory: fabricatedColorsCategory(data.colorScheme)
36237
- }),
36508
+ data: buildFabricatedDiagramDataXml(
36509
+ data,
36510
+ {
36511
+ layoutUniqueId: fabricatedLayoutUniqueId(family),
36512
+ layoutCategory: fabricatedLayoutCategory(family),
36513
+ quickStyleUniqueId: FABRICATED_QUICKSTYLE_UNIQUE_ID,
36514
+ colorsUniqueId: fabricatedColorsUniqueId(data.colorScheme),
36515
+ colorsCategory: fabricatedColorsCategory(data.colorScheme)
36516
+ },
36517
+ { guidByNodeId, drawingRelId: drawingXml ? drawingRelId : void 0 }
36518
+ ),
36238
36519
  layout: buildFabricatedLayoutDefXml(family),
36239
36520
  quickStyle: buildFabricatedQuickStyleXml(),
36240
36521
  colors: buildFabricatedColorsXml(data.colorScheme)
@@ -36258,6 +36539,19 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
36258
36539
  });
36259
36540
  relIds[kind.relAttr] = relId;
36260
36541
  }
36542
+ if (drawingXml) {
36543
+ const drawingFile = `drawing${index}.xml`;
36544
+ const drawingPath = `ppt/diagrams/${drawingFile}`;
36545
+ this.zip.file(drawingPath, drawingXml);
36546
+ (this.pendingDiagramContentTypes ??= []).push({
36547
+ partName: `/${drawingPath}`,
36548
+ contentType: DIAGRAM_DRAWING_CONTENT_TYPE
36549
+ });
36550
+ this.zip.file(
36551
+ `ppt/diagrams/_rels/data${index}.xml.rels`,
36552
+ buildDiagramDataRelsXml(drawingRelId, drawingFile)
36553
+ );
36554
+ }
36261
36555
  const EMU = _PptxHandlerRuntime.EMU_PER_PX;
36262
36556
  return {
36263
36557
  "p:nvGraphicFramePr": {
@@ -37613,6 +37907,9 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
37613
37907
  slideLayoutRelationshipType,
37614
37908
  relationshipsNamespace
37615
37909
  });
37910
+ for (const slide of slides) {
37911
+ await this.processSlideForSave(slide, saveSession, saveConstants);
37912
+ }
37616
37913
  const contentTypesXml2 = await this.zip.file("[Content_Types].xml")?.async("string");
37617
37914
  if (contentTypesXml2) {
37618
37915
  const contentTypesData = this.parser.parse(contentTypesXml2);
@@ -37624,9 +37921,6 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
37624
37921
  });
37625
37922
  this.zip.file("[Content_Types].xml", this.builder.build(contentTypesData));
37626
37923
  }
37627
- for (const slide of slides) {
37628
- await this.processSlideForSave(slide, saveSession, saveConstants);
37629
- }
37630
37924
  for (const existingCommentPath of saveSession.getExistingCommentPaths()) {
37631
37925
  if (saveSession.isCommentPathActive(existingCommentPath)) {
37632
37926
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pptx-viewer-core",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "PowerPoint PPTX engine: parse, edit, serialize, and convert .pptx files. Framework-agnostic TypeScript SDK.",
5
5
  "keywords": [
6
6
  "angular",