pptx-glimpse 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  collectUsedFonts
3
- } from "./chunk-ZZKN4U5J.js";
3
+ } from "./chunk-KY3SYR3V.js";
4
4
  import {
5
5
  DEFAULT_OUTPUT_WIDTH,
6
6
  addConnector,
@@ -10,22 +10,27 @@ import {
10
10
  asPartPath,
11
11
  asRelationshipId,
12
12
  asSourceNodeId,
13
+ clearParagraphProperties,
13
14
  clearTextRunProperties,
14
15
  convertPptxToSvg,
15
16
  deleteShape,
16
17
  deleteSlide,
17
18
  duplicateSlide,
18
19
  findShapeNodeBySourceHandle,
20
+ moveSlide,
19
21
  readPptx,
20
22
  renderPptxSourceModelToSvg,
21
23
  replaceImageBytes,
22
24
  replaceParagraphPlainText,
23
25
  replaceTextRunPlainText,
26
+ setParagraphProperties,
27
+ setShapeFill,
28
+ setShapeOutline,
24
29
  setTextRunProperties,
25
30
  unsafeBrandAssertion,
26
31
  updateShapeTransform,
27
32
  writePptx
28
- } from "./chunk-NMAO44T7.js";
33
+ } from "./chunk-CX4ZDTKK.js";
29
34
  import {
30
35
  DEFAULT_FONT_MAPPING,
31
36
  clearFontCache,
@@ -2542,6 +2547,23 @@ var pptxTextBodySchema = new Schema({
2542
2547
  }
2543
2548
  }
2544
2549
  });
2550
+ var EDITABLE_PARAGRAPH_PROPERTIES = [
2551
+ "align",
2552
+ "level",
2553
+ "bullet"
2554
+ ];
2555
+ var AUTO_NUM_SCHEMES = [
2556
+ "arabicPeriod",
2557
+ "arabicParenR",
2558
+ "romanUcPeriod",
2559
+ "romanLcPeriod",
2560
+ "alphaUcPeriod",
2561
+ "alphaLcPeriod",
2562
+ "alphaLcParenR",
2563
+ "alphaUcParenR",
2564
+ "arabicPlain"
2565
+ ];
2566
+ var AUTO_NUM_SCHEME_SET = new Set(AUTO_NUM_SCHEMES);
2545
2567
  function textBodyToProseMirrorDocJson(textBody) {
2546
2568
  assertSupportedTextBody(textBody);
2547
2569
  return {
@@ -2607,8 +2629,12 @@ function proseMirrorDocJsonToEditorCommands(originalTextBody, docJson) {
2607
2629
  (editedParagraph, paragraphIndex) => {
2608
2630
  const originalParagraph = originalTextBody.paragraphs[paragraphIndex];
2609
2631
  if (originalParagraph === void 0) return [];
2632
+ const paragraphPropertyCommands = paragraphPropertiesToEditorCommands(
2633
+ originalParagraph,
2634
+ editedParagraph
2635
+ );
2610
2636
  if (paragraphRunHandlesMatch(originalParagraph, editedParagraph)) {
2611
- return editedParagraph.runs.flatMap(
2637
+ const textRunCommands = editedParagraph.runs.flatMap(
2612
2638
  (editedRun, runIndex) => {
2613
2639
  const originalRun = originalParagraph.runs[runIndex];
2614
2640
  if (originalRun === void 0 || editedRun.text === originalRun.text) return [];
@@ -2626,6 +2652,7 @@ function proseMirrorDocJsonToEditorCommands(originalTextBody, docJson) {
2626
2652
  ];
2627
2653
  }
2628
2654
  );
2655
+ return [...paragraphPropertyCommands, ...textRunCommands];
2629
2656
  }
2630
2657
  if (editedParagraph.handle === void 0) {
2631
2658
  throw new Error(
@@ -2633,6 +2660,7 @@ function proseMirrorDocJsonToEditorCommands(originalTextBody, docJson) {
2633
2660
  );
2634
2661
  }
2635
2662
  return [
2663
+ ...paragraphPropertyCommands,
2636
2664
  {
2637
2665
  kind: "replaceParagraphPlainText",
2638
2666
  handle: editedParagraph.handle,
@@ -2648,10 +2676,14 @@ function validateProseMirrorDocJson(docJson) {
2648
2676
  }
2649
2677
  function paragraphJsonToSourceParagraph(originalParagraph, paragraphJson) {
2650
2678
  const groups = collectRunGroups(paragraphJson, originalParagraph);
2679
+ const properties = paragraphPropertiesFromJson(
2680
+ originalParagraph?.properties,
2681
+ paragraphJson.attrs?.properties
2682
+ );
2651
2683
  return {
2652
- ...originalParagraph ?? {},
2653
2684
  runs: groups.map(sourceRunFromGroup),
2654
- ...originalParagraph?.properties !== void 0 ? { properties: originalParagraph.properties } : {},
2685
+ ...originalParagraph?.rawSidecars !== void 0 ? { rawSidecars: originalParagraph.rawSidecars } : {},
2686
+ ...properties !== void 0 ? { properties } : {},
2655
2687
  ...originalParagraph?.handle !== void 0 ? { handle: originalParagraph.handle } : sourceHandleFromUnknown(paragraphJson.attrs?.handle) !== void 0 ? { handle: sourceHandleFromUnknown(paragraphJson.attrs?.handle) } : {}
2656
2688
  };
2657
2689
  }
@@ -2701,6 +2733,107 @@ function paragraphRunHandlesMatch(originalParagraph, editedParagraph) {
2701
2733
  function paragraphPlainText(paragraph) {
2702
2734
  return paragraph.runs.map((run) => run.text).join("");
2703
2735
  }
2736
+ function paragraphPropertiesToEditorCommands(originalParagraph, editedParagraph) {
2737
+ if (editedParagraph.handle === void 0) {
2738
+ if (paragraphEditablePropertiesEqual(originalParagraph.properties, editedParagraph.properties)) {
2739
+ return [];
2740
+ }
2741
+ throw new Error("proseMirrorDocJsonToEditorCommands: changed paragraph has no source handle");
2742
+ }
2743
+ const set = {};
2744
+ const clear = [];
2745
+ if (!stableValueEqual(originalParagraph.properties?.align, editedParagraph.properties?.align)) {
2746
+ if (editedParagraph.properties?.align === void 0) clear.push("align");
2747
+ else set.align = editedParagraph.properties.align;
2748
+ }
2749
+ if (!stableValueEqual(originalParagraph.properties?.level, editedParagraph.properties?.level)) {
2750
+ if (editedParagraph.properties?.level === void 0) clear.push("level");
2751
+ else set.level = editedParagraph.properties.level;
2752
+ }
2753
+ if (!stableValueEqual(originalParagraph.properties?.bullet, editedParagraph.properties?.bullet)) {
2754
+ if (editedParagraph.properties?.bullet === void 0) clear.push("bullet");
2755
+ else set.bullet = editedParagraph.properties.bullet;
2756
+ }
2757
+ const commands = [];
2758
+ if (clear.length > 0) {
2759
+ commands.push({
2760
+ kind: "clearParagraphProperties",
2761
+ handle: editedParagraph.handle,
2762
+ properties: clear
2763
+ });
2764
+ }
2765
+ if (Object.keys(set).length > 0) {
2766
+ commands.push({
2767
+ kind: "setParagraphProperties",
2768
+ handle: editedParagraph.handle,
2769
+ properties: set
2770
+ });
2771
+ }
2772
+ return commands;
2773
+ }
2774
+ function paragraphPropertiesFromJson(originalProperties, value) {
2775
+ if (!isRecord(value)) return originalProperties;
2776
+ const next = { ...originalProperties ?? {} };
2777
+ for (const property of EDITABLE_PARAGRAPH_PROPERTIES) {
2778
+ if (!Object.prototype.hasOwnProperty.call(value, property)) {
2779
+ delete next[property];
2780
+ continue;
2781
+ }
2782
+ const propertyValue = value[property];
2783
+ if (propertyValue === null || propertyValue === void 0) {
2784
+ delete next[property];
2785
+ } else if (property === "align") {
2786
+ next.align = paragraphAlignFromUnknown(propertyValue);
2787
+ } else if (property === "level") {
2788
+ next.level = paragraphLevelFromUnknown(propertyValue);
2789
+ } else {
2790
+ next.bullet = paragraphBulletFromUnknown(propertyValue);
2791
+ }
2792
+ }
2793
+ return Object.keys(next).length > 0 ? next : void 0;
2794
+ }
2795
+ function paragraphAlignFromUnknown(value) {
2796
+ if (value === "left" || value === "center" || value === "right" || value === "justify") {
2797
+ return value;
2798
+ }
2799
+ throw new Error("ProseMirror paragraph properties align must be left, center, right, or justify");
2800
+ }
2801
+ function paragraphLevelFromUnknown(value) {
2802
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 8) {
2803
+ return value;
2804
+ }
2805
+ throw new Error("ProseMirror paragraph properties level must be an integer from 0 to 8");
2806
+ }
2807
+ function paragraphBulletFromUnknown(value) {
2808
+ if (!isRecord(value) || typeof value.type !== "string") {
2809
+ throw new Error("ProseMirror paragraph properties bullet must be an object");
2810
+ }
2811
+ if (value.type === "none") return { type: "none" };
2812
+ if (value.type === "char") {
2813
+ if (typeof value.char !== "string" || value.char.length === 0) {
2814
+ throw new Error("ProseMirror paragraph properties bullet.char must be a non-empty string");
2815
+ }
2816
+ return { type: "char", char: value.char };
2817
+ }
2818
+ if (value.type === "autoNum") {
2819
+ if (!isAutoNumScheme(value.scheme)) {
2820
+ throw new Error("ProseMirror paragraph properties bullet.scheme is unsupported");
2821
+ }
2822
+ if (typeof value.startAt !== "number" || !Number.isInteger(value.startAt) || value.startAt < 1) {
2823
+ throw new Error("ProseMirror paragraph properties bullet.startAt must be positive");
2824
+ }
2825
+ return { type: "autoNum", scheme: value.scheme, startAt: value.startAt };
2826
+ }
2827
+ throw new Error("ProseMirror paragraph properties bullet.type is unsupported");
2828
+ }
2829
+ function isAutoNumScheme(value) {
2830
+ return typeof value === "string" && AUTO_NUM_SCHEME_SET.has(value);
2831
+ }
2832
+ function paragraphEditablePropertiesEqual(left, right) {
2833
+ return EDITABLE_PARAGRAPH_PROPERTIES.every(
2834
+ (property) => stableValueEqual(left?.[property], right?.[property])
2835
+ );
2836
+ }
2704
2837
  function parsePptxTextBodyProseMirrorDocJson(value) {
2705
2838
  if (!isRecord(value) || value.type !== "doc") {
2706
2839
  throw new Error("ProseMirror text body doc JSON must be a doc node");
@@ -2708,7 +2841,7 @@ function parsePptxTextBodyProseMirrorDocJson(value) {
2708
2841
  if (value.content !== void 0 && readArray(value.content, isParagraphJson) === void 0) {
2709
2842
  throw new Error("ProseMirror text body doc JSON content must contain paragraph nodes");
2710
2843
  }
2711
- const content = readArray(value.content, isParagraphJson);
2844
+ const content = readArray(value.content, isParagraphJson)?.map(normalizeParagraphJson);
2712
2845
  return {
2713
2846
  type: "doc",
2714
2847
  ...content !== void 0 ? { content } : {}
@@ -2721,6 +2854,13 @@ function isParagraphJson(value) {
2721
2854
  }
2722
2855
  return value.attrs === void 0 || isRecord(value.attrs);
2723
2856
  }
2857
+ function normalizeParagraphJson(value) {
2858
+ return {
2859
+ type: "paragraph",
2860
+ ...value.attrs !== void 0 ? { attrs: value.attrs } : {},
2861
+ ...value.content !== void 0 ? { content: value.content } : {}
2862
+ };
2863
+ }
2724
2864
  function isTextJson(value) {
2725
2865
  if (!isRecord(value) || value.type !== "text" || typeof value.text !== "string") return false;
2726
2866
  if (value.marks === void 0) return true;
@@ -2758,6 +2898,22 @@ function sourceHandleKey(handle) {
2758
2898
  handle.orderingSlot ?? ""
2759
2899
  ].join("\0");
2760
2900
  }
2901
+ function stableValueEqual(left, right) {
2902
+ if (Object.is(left, right)) return true;
2903
+ if (Array.isArray(left) || Array.isArray(right)) {
2904
+ if (!Array.isArray(left) || !Array.isArray(right)) return false;
2905
+ if (left.length !== right.length) return false;
2906
+ return left.every((value, index) => stableValueEqual(value, right[index]));
2907
+ }
2908
+ if (isRecord(left) || isRecord(right)) {
2909
+ if (!isRecord(left) || !isRecord(right)) return false;
2910
+ const leftKeys = Object.keys(left).sort();
2911
+ const rightKeys = Object.keys(right).sort();
2912
+ if (!stableValueEqual(leftKeys, rightKeys)) return false;
2913
+ return leftKeys.every((key) => stableValueEqual(left[key], right[key]));
2914
+ }
2915
+ return false;
2916
+ }
2761
2917
  function isRecord(value) {
2762
2918
  return typeof value === "object" && value !== null && !Array.isArray(value);
2763
2919
  }
@@ -2871,12 +3027,20 @@ function applyCommandToDocument(document, command) {
2871
3027
  return setTextRunPropertiesCommand(document, command);
2872
3028
  case "clearTextRunProperties":
2873
3029
  return clearTextRunPropertiesCommand(document, command);
3030
+ case "setParagraphProperties":
3031
+ return setParagraphPropertiesCommand(document, command);
3032
+ case "clearParagraphProperties":
3033
+ return clearParagraphPropertiesCommand(document, command);
2874
3034
  case "moveShape":
2875
3035
  return moveShape(document, command);
2876
3036
  case "resizeShape":
2877
3037
  return resizeShape(document, command);
2878
3038
  case "setShapeTransform":
2879
3039
  return setShapeTransform(document, command);
3040
+ case "setShapeFill":
3041
+ return setShapeFillCommand(document, command);
3042
+ case "setShapeOutline":
3043
+ return setShapeOutlineCommand(document, command);
2880
3044
  case "addTextBox":
2881
3045
  return addTextBoxCommand(document, command);
2882
3046
  case "addConnector":
@@ -2889,6 +3053,8 @@ function applyCommandToDocument(document, command) {
2889
3053
  return addEmptySlideFromLayout(document, command);
2890
3054
  case "duplicateSlide":
2891
3055
  return duplicateSlide(document, command.handle);
3056
+ case "moveSlide":
3057
+ return moveSlide(document, command.handle, command);
2892
3058
  case "deleteSlide":
2893
3059
  return deleteSlide(document, command.handle);
2894
3060
  }
@@ -2961,6 +3127,22 @@ function clearTextRunPropertiesCommand(document, command) {
2961
3127
  }
2962
3128
  return clearTextRunProperties(document, command.handle, command.properties);
2963
3129
  }
3130
+ function setParagraphPropertiesCommand(document, command) {
3131
+ requireNonEmptyParagraphPropertySet(command.properties, "setParagraphProperties");
3132
+ validateParagraphPropertySet(command.properties, "setParagraphProperties");
3133
+ return setParagraphProperties(document, command.handle, command.properties);
3134
+ }
3135
+ function clearParagraphPropertiesCommand(document, command) {
3136
+ if (command.properties.length === 0) {
3137
+ throw new Error("clearParagraphProperties: properties must contain at least one property name");
3138
+ }
3139
+ for (const property of command.properties) {
3140
+ if (!EDITABLE_PARAGRAPH_PROPERTY_SET.has(property)) {
3141
+ throw new Error(`clearParagraphProperties: unsupported paragraph property '${property}'`);
3142
+ }
3143
+ }
3144
+ return clearParagraphProperties(document, command.handle, command.properties);
3145
+ }
2964
3146
  function moveShape(document, command) {
2965
3147
  requireFiniteEmu(command.offsetX, "moveShape", "offsetX");
2966
3148
  requireFiniteEmu(command.offsetY, "moveShape", "offsetY");
@@ -2996,6 +3178,14 @@ function setShapeTransform(document, command) {
2996
3178
  height: command.height
2997
3179
  });
2998
3180
  }
3181
+ function setShapeFillCommand(document, command) {
3182
+ validateShapeFill(command.fill, "setShapeFill");
3183
+ return setShapeFill(document, command.handle, command.fill);
3184
+ }
3185
+ function setShapeOutlineCommand(document, command) {
3186
+ validateShapeOutline(command.outline, "setShapeOutline");
3187
+ return setShapeOutline(document, command.handle, command.outline);
3188
+ }
2999
3189
  var EDITABLE_TEXT_RUN_PROPERTIES = [
3000
3190
  "bold",
3001
3191
  "italic",
@@ -3005,6 +3195,24 @@ var EDITABLE_TEXT_RUN_PROPERTIES = [
3005
3195
  "typeface"
3006
3196
  ];
3007
3197
  var EDITABLE_TEXT_RUN_PROPERTY_SET = new Set(EDITABLE_TEXT_RUN_PROPERTIES);
3198
+ var EDITABLE_PARAGRAPH_PROPERTIES2 = [
3199
+ "align",
3200
+ "level",
3201
+ "bullet"
3202
+ ];
3203
+ var EDITABLE_PARAGRAPH_PROPERTY_SET = new Set(EDITABLE_PARAGRAPH_PROPERTIES2);
3204
+ var PARAGRAPH_ALIGN_VALUES = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
3205
+ var AUTO_NUM_SCHEMES2 = /* @__PURE__ */ new Set([
3206
+ "arabicPeriod",
3207
+ "arabicParenR",
3208
+ "romanUcPeriod",
3209
+ "romanLcPeriod",
3210
+ "alphaUcPeriod",
3211
+ "alphaLcPeriod",
3212
+ "alphaLcParenR",
3213
+ "alphaUcParenR",
3214
+ "arabicPlain"
3215
+ ]);
3008
3216
  function requireNonEmptyPropertySet(properties, commandName) {
3009
3217
  if (Object.values(properties).every((value) => value === void 0)) {
3010
3218
  throw new Error(`${commandName}: properties must contain at least one defined property`);
@@ -3034,11 +3242,72 @@ function validateTextRunPropertySet(properties, commandName) {
3034
3242
  }
3035
3243
  }
3036
3244
  }
3245
+ function requireNonEmptyParagraphPropertySet(properties, commandName) {
3246
+ if (Object.values(properties).every((value) => value === void 0)) {
3247
+ throw new Error(`${commandName}: properties must contain at least one defined property`);
3248
+ }
3249
+ }
3250
+ function validateParagraphPropertySet(properties, commandName) {
3251
+ for (const property of Object.keys(properties)) {
3252
+ if (!EDITABLE_PARAGRAPH_PROPERTY_SET.has(property)) {
3253
+ throw new Error(`${commandName}: unsupported paragraph property '${property}'`);
3254
+ }
3255
+ }
3256
+ if (properties.align !== void 0 && !PARAGRAPH_ALIGN_VALUES.has(properties.align)) {
3257
+ throw new Error(`${commandName}: align must be left, center, right, or justify`);
3258
+ }
3259
+ if (properties.level !== void 0 && (!Number.isInteger(properties.level) || properties.level < 0 || properties.level > 8)) {
3260
+ throw new Error(`${commandName}: level must be an integer from 0 to 8`);
3261
+ }
3262
+ if (properties.bullet !== void 0) {
3263
+ validateParagraphBullet(properties.bullet, commandName);
3264
+ }
3265
+ }
3266
+ function validateParagraphBullet(bullet, commandName) {
3267
+ if (bullet.type === "none") return;
3268
+ if (bullet.type === "char") {
3269
+ if (bullet.char.length === 0) {
3270
+ throw new Error(`${commandName}: bullet.char must be a non-empty string`);
3271
+ }
3272
+ return;
3273
+ }
3274
+ if (bullet.type === "autoNum") {
3275
+ if (!AUTO_NUM_SCHEMES2.has(bullet.scheme)) {
3276
+ throw new Error(`${commandName}: unsupported bullet auto-numbering scheme`);
3277
+ }
3278
+ if (!Number.isInteger(bullet.startAt) || bullet.startAt < 1) {
3279
+ throw new Error(`${commandName}: bullet.startAt must be a positive integer`);
3280
+ }
3281
+ return;
3282
+ }
3283
+ throw new Error(`${commandName}: unsupported bullet type`);
3284
+ }
3037
3285
  function requireBooleanOrUndefined(value, commandName, fieldName) {
3038
3286
  if (value !== void 0 && typeof value !== "boolean") {
3039
3287
  throw new Error(`${commandName}: ${fieldName} must be a boolean value`);
3040
3288
  }
3041
3289
  }
3290
+ function validateShapeOutline(outline, commandName) {
3291
+ if (outline.width === void 0 && outline.fill === void 0) {
3292
+ throw new Error(`${commandName}: outline must set width or fill`);
3293
+ }
3294
+ if (outline.width !== void 0) {
3295
+ requirePositiveFiniteEmu(outline.width, commandName, "width");
3296
+ }
3297
+ if (outline.fill !== void 0) validateShapeFill(outline.fill, commandName);
3298
+ }
3299
+ function validateShapeFill(fill, commandName) {
3300
+ if (fill.kind === "none") return;
3301
+ if (fill.kind !== "solid") {
3302
+ throw new Error(`${commandName}: only solid and none fills are supported`);
3303
+ }
3304
+ if (fill.color.kind !== "srgb") {
3305
+ throw new Error(`${commandName}: only srgb solid fill colors are supported`);
3306
+ }
3307
+ if (!/^[0-9A-Fa-f]{6}$/.test(fill.color.hex)) {
3308
+ throw new Error(`${commandName}: color.hex must be a 6-digit hex value`);
3309
+ }
3310
+ }
3042
3311
  function requireEditableShapeTransform(document, handle, commandName) {
3043
3312
  const shape = findShapeNodeBySourceHandle(document, handle);
3044
3313
  if (shape === void 0) {
@@ -3067,8 +3336,12 @@ function normalizeEditorEdits(document) {
3067
3336
  if (edits === void 0) return document;
3068
3337
  const seenTextRuns = /* @__PURE__ */ new Set();
3069
3338
  const seenTextRunProperties = /* @__PURE__ */ new Map();
3339
+ const seenParagraphProperties = /* @__PURE__ */ new Map();
3070
3340
  const seenParagraphs = /* @__PURE__ */ new Set();
3071
3341
  const seenShapeTransforms = /* @__PURE__ */ new Set();
3342
+ const seenShapeFills = /* @__PURE__ */ new Set();
3343
+ const seenShapeOutlineProperties = /* @__PURE__ */ new Map();
3344
+ const normalizedShapeOutlineEdits = /* @__PURE__ */ new Map();
3072
3345
  const normalizedReversed = [];
3073
3346
  let changed = false;
3074
3347
  for (let index = edits.length - 1; index >= 0; index -= 1) {
@@ -3101,6 +3374,16 @@ function normalizeEditorEdits(document) {
3101
3374
  normalizedReversed.push(normalized);
3102
3375
  continue;
3103
3376
  }
3377
+ if (edit.kind === "updateParagraphProperties") {
3378
+ const normalized = normalizeParagraphPropertiesEdit(edit, seenParagraphProperties);
3379
+ if (normalized === void 0) {
3380
+ changed = true;
3381
+ continue;
3382
+ }
3383
+ if (!editorEditsEqual(normalized, edit)) changed = true;
3384
+ normalizedReversed.push(normalized);
3385
+ continue;
3386
+ }
3104
3387
  if (edit.kind === "replaceParagraphPlainText") {
3105
3388
  const key = editHandleNodeKey(edit);
3106
3389
  if (seenParagraphs.has(key)) {
@@ -3117,6 +3400,32 @@ function normalizeEditorEdits(document) {
3117
3400
  }
3118
3401
  seenShapeTransforms.add(key);
3119
3402
  }
3403
+ if (edit.kind === "updateShapeFill") {
3404
+ const key = editHandleNodeKey(edit);
3405
+ if (seenShapeFills.has(key)) {
3406
+ changed = true;
3407
+ continue;
3408
+ }
3409
+ seenShapeFills.add(key);
3410
+ }
3411
+ if (edit.kind === "updateShapeOutline") {
3412
+ const normalized = normalizeShapeOutlineEdit(
3413
+ edit,
3414
+ seenShapeOutlineProperties,
3415
+ normalizedShapeOutlineEdits
3416
+ );
3417
+ if (normalized === void 0) {
3418
+ changed = true;
3419
+ continue;
3420
+ }
3421
+ if (normalized.merged) {
3422
+ changed = true;
3423
+ continue;
3424
+ }
3425
+ if (!editorEditsEqual(normalized.edit, edit)) changed = true;
3426
+ normalizedReversed.push(normalized.edit);
3427
+ continue;
3428
+ }
3120
3429
  normalizedReversed.push(edit);
3121
3430
  }
3122
3431
  if (!changed && normalizedReversed.length === edits.length) return document;
@@ -3193,6 +3502,67 @@ function normalizeTextRunPropertiesEdit(edit, seenTextRunProperties) {
3193
3502
  };
3194
3503
  return normalized;
3195
3504
  }
3505
+ function normalizeParagraphPropertiesEdit(edit, seenParagraphProperties) {
3506
+ const key = editHandleNodeKey(edit);
3507
+ let seenProperties = seenParagraphProperties.get(key);
3508
+ if (seenProperties === void 0) {
3509
+ seenProperties = /* @__PURE__ */ new Set();
3510
+ seenParagraphProperties.set(key, seenProperties);
3511
+ }
3512
+ const set = {};
3513
+ if (edit.set?.align !== void 0 && !seenProperties.has("align")) {
3514
+ seenProperties.add("align");
3515
+ set.align = edit.set.align;
3516
+ }
3517
+ if (edit.set?.level !== void 0 && !seenProperties.has("level")) {
3518
+ seenProperties.add("level");
3519
+ set.level = edit.set.level;
3520
+ }
3521
+ if (edit.set?.bullet !== void 0 && !seenProperties.has("bullet")) {
3522
+ seenProperties.add("bullet");
3523
+ set.bullet = edit.set.bullet;
3524
+ }
3525
+ const clear = (edit.clear ?? []).filter((property) => !seenProperties.has(property));
3526
+ for (const property of clear) seenProperties.add(property);
3527
+ if (clear.length === 0 && Object.keys(set).length === 0) return void 0;
3528
+ const normalized = {
3529
+ kind: "updateParagraphProperties",
3530
+ handle: edit.handle,
3531
+ ...Object.keys(set).length > 0 ? { set } : {},
3532
+ ...clear.length > 0 ? { clear } : {}
3533
+ };
3534
+ return normalized;
3535
+ }
3536
+ function normalizeShapeOutlineEdit(edit, seenShapeOutlineProperties, normalizedShapeOutlineEdits) {
3537
+ const key = editHandleNodeKey(edit);
3538
+ let seenProperties = seenShapeOutlineProperties.get(key);
3539
+ if (seenProperties === void 0) {
3540
+ seenProperties = /* @__PURE__ */ new Set();
3541
+ seenShapeOutlineProperties.set(key, seenProperties);
3542
+ }
3543
+ const outline = {};
3544
+ if (edit.outline.width !== void 0 && !seenProperties.has("width")) {
3545
+ seenProperties.add("width");
3546
+ outline.width = edit.outline.width;
3547
+ }
3548
+ if (edit.outline.fill !== void 0 && !seenProperties.has("fill")) {
3549
+ seenProperties.add("fill");
3550
+ outline.fill = edit.outline.fill;
3551
+ }
3552
+ if (Object.keys(outline).length === 0) return void 0;
3553
+ const existing = normalizedShapeOutlineEdits.get(key);
3554
+ if (existing !== void 0) {
3555
+ existing.outline = { ...outline, ...existing.outline };
3556
+ return { merged: true };
3557
+ }
3558
+ const normalized = {
3559
+ kind: "updateShapeOutline",
3560
+ handle: edit.handle,
3561
+ outline
3562
+ };
3563
+ normalizedShapeOutlineEdits.set(key, normalized);
3564
+ return { edit: normalized, merged: false };
3565
+ }
3196
3566
 
3197
3567
  // src/browser-editor.ts
3198
3568
  var EMU_PER_INCH = 914400;
@@ -3205,6 +3575,12 @@ var DEFAULT_TEXT_BOX_BOUNDS_PX = {
3205
3575
  height: 72
3206
3576
  };
3207
3577
  var DEFAULT_TEXT_BOX_TEXT = "New text box";
3578
+ var DEFAULT_CONNECTOR_BOUNDS_PX = {
3579
+ x: 144,
3580
+ y: 144,
3581
+ width: 288,
3582
+ height: 96
3583
+ };
3208
3584
  var IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
3209
3585
  var IMAGE_ACCEPT_BY_CONTENT_TYPE = {
3210
3586
  "image/png": "image/png,.png",
@@ -3302,6 +3678,32 @@ var BrowserPptxEditorSession = class _BrowserPptxEditorSession {
3302
3678
  await this.renderCurrentSlides();
3303
3679
  return this.response(result.warnings);
3304
3680
  }
3681
+ async addConnector(slideNumber = 1, options = {}) {
3682
+ const slide = this.#session.document.slides[slideNumber - 1];
3683
+ if (slide?.handle === void 0) {
3684
+ throw new Error("addConnector: slide handle was not found in PptxSourceModel source");
3685
+ }
3686
+ const existingShapeKeys = new Set(slide.shapes.map(shapeSourceKey));
3687
+ const result = this.#session.apply({
3688
+ kind: "addConnector",
3689
+ slideHandle: slide.handle,
3690
+ preset: "straightConnector1",
3691
+ offsetX: pxToEmu(options.x ?? DEFAULT_CONNECTOR_BOUNDS_PX.x),
3692
+ offsetY: pxToEmu(options.y ?? DEFAULT_CONNECTOR_BOUNDS_PX.y),
3693
+ width: pxToEmu(options.width ?? DEFAULT_CONNECTOR_BOUNDS_PX.width),
3694
+ height: pxToEmu(options.height ?? DEFAULT_CONNECTOR_BOUNDS_PX.height),
3695
+ outline: {
3696
+ tailEnd: { type: "triangle", width: "med", length: "med" }
3697
+ },
3698
+ ...options.name !== void 0 ? { name: options.name } : {}
3699
+ });
3700
+ if (!result.ok) {
3701
+ throw new Error(result.message);
3702
+ }
3703
+ this.#selectNewShape(slideNumber, existingShapeKeys);
3704
+ await this.renderCurrentSlides();
3705
+ return this.response(result.warnings);
3706
+ }
3305
3707
  async deleteShape(handle) {
3306
3708
  const result = this.#session.apply({ kind: "deleteShape", handle });
3307
3709
  if (!result.ok) {
@@ -3414,10 +3816,10 @@ function isEditableTransformShape(shape) {
3414
3816
  return !shape.rawSidecars?.some((sidecar) => sidecar.node.name === "mc:AlternateContent");
3415
3817
  }
3416
3818
  function isDeletableShape(shape, slideShapes) {
3417
- if (shape.kind !== "shape" || shape.handle?.nodeId === void 0) {
3819
+ if (shape.kind !== "shape" && shape.kind !== "connector" || shape.handle?.nodeId === void 0) {
3418
3820
  return false;
3419
3821
  }
3420
- if (isShapeReferencedByConnector(shape, slideShapes)) {
3822
+ if (shape.kind === "shape" && isShapeReferencedByConnector(shape, slideShapes)) {
3421
3823
  return false;
3422
3824
  }
3423
3825
  return !shape.rawSidecars?.some((sidecar) => sidecar.node.name === "mc:AlternateContent");