pptx-glimpse 3.1.1 → 3.2.1

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.cjs CHANGED
@@ -5938,6 +5938,16 @@ var EDIT_KIND_DESCRIPTORS = {
5938
5938
  insertedSlidePartPath: () => void 0,
5939
5939
  insertedShape: () => void 0
5940
5940
  },
5941
+ updateParagraphProperties: {
5942
+ reservedPartPaths: () => [],
5943
+ dirtyPartPath: (edit) => edit.handle.partPath,
5944
+ targetsShape: (edit, shapeHandle) => edit.handle.partPath === shapeHandle.partPath && paragraphShapeId(edit.handle) === shapeHandle.nodeId,
5945
+ invalidatingPartPaths: (edit) => [edit.handle.partPath],
5946
+ reservedShapeId: () => void 0,
5947
+ slideTopologyOperation: () => void 0,
5948
+ insertedSlidePartPath: () => void 0,
5949
+ insertedShape: () => void 0
5950
+ },
5941
5951
  replaceParagraphPlainText: {
5942
5952
  reservedPartPaths: () => [],
5943
5953
  dirtyPartPath: (edit) => edit.handle.partPath,
@@ -5958,6 +5968,26 @@ var EDIT_KIND_DESCRIPTORS = {
5958
5968
  insertedSlidePartPath: () => void 0,
5959
5969
  insertedShape: () => void 0
5960
5970
  },
5971
+ updateShapeFill: {
5972
+ reservedPartPaths: () => [],
5973
+ dirtyPartPath: (edit) => edit.handle.partPath,
5974
+ targetsShape: (edit, shapeHandle) => sourceHandlesEqual(edit.handle, shapeHandle),
5975
+ invalidatingPartPaths: (edit) => [edit.handle.partPath],
5976
+ reservedShapeId: () => void 0,
5977
+ slideTopologyOperation: () => void 0,
5978
+ insertedSlidePartPath: () => void 0,
5979
+ insertedShape: () => void 0
5980
+ },
5981
+ updateShapeOutline: {
5982
+ reservedPartPaths: () => [],
5983
+ dirtyPartPath: (edit) => edit.handle.partPath,
5984
+ targetsShape: (edit, shapeHandle) => sourceHandlesEqual(edit.handle, shapeHandle),
5985
+ invalidatingPartPaths: (edit) => [edit.handle.partPath],
5986
+ reservedShapeId: () => void 0,
5987
+ slideTopologyOperation: () => void 0,
5988
+ insertedSlidePartPath: () => void 0,
5989
+ insertedShape: () => void 0
5990
+ },
5961
5991
  addTextBox: {
5962
5992
  reservedPartPaths: (edit) => [edit.slidePartPath],
5963
5993
  dirtyPartPath: (edit) => edit.slidePartPath,
@@ -6027,6 +6057,20 @@ var EDIT_KIND_DESCRIPTORS = {
6027
6057
  insertedSlidePartPath: (edit) => edit.newSlidePartPath,
6028
6058
  insertedShape: () => void 0
6029
6059
  },
6060
+ moveSlide: {
6061
+ reservedPartPaths: (edit) => [edit.slidePartPath],
6062
+ dirtyPartPath: () => void 0,
6063
+ targetsShape: () => false,
6064
+ invalidatingPartPaths: (edit) => [edit.slidePartPath],
6065
+ reservedShapeId: () => void 0,
6066
+ slideTopologyOperation: (edit) => ({
6067
+ kind: "moveSlide",
6068
+ relationshipId: edit.relationshipId,
6069
+ toIndex: edit.toIndex
6070
+ }),
6071
+ insertedSlidePartPath: () => void 0,
6072
+ insertedShape: () => void 0
6073
+ },
6030
6074
  deleteSlide: {
6031
6075
  reservedPartPaths: (edit) => [edit.slidePartPath],
6032
6076
  dirtyPartPath: () => void 0,
@@ -6664,129 +6708,560 @@ function parseEnumValue(value, allowed) {
6664
6708
  function parseEnumValueWithDefault(value, allowed, fallback) {
6665
6709
  return parseEnumValue(value, allowed) ?? fallback;
6666
6710
  }
6667
- var KNOWN_TXBODY_CHILDREN = /* @__PURE__ */ new Set(["bodyPr", "lstStyle", "p"]);
6668
- var KNOWN_PARAGRAPH_CHILDREN = /* @__PURE__ */ new Set([
6669
- "pPr",
6711
+ var RECTANGLE_ALIGNMENT_TOKENS = /* @__PURE__ */ new Set([
6712
+ "tl",
6713
+ "t",
6714
+ "tr",
6715
+ "l",
6716
+ "ctr",
6670
6717
  "r",
6671
- "fld",
6672
- "br",
6673
- "endParaRPr"
6718
+ "bl",
6719
+ "b",
6720
+ "br"
6674
6721
  ]);
6675
- var KNOWN_RUN_CHILDREN = /* @__PURE__ */ new Set(["rPr", "t"]);
6676
- var KNOWN_RUN_PROPERTY_CHILDREN = /* @__PURE__ */ new Set([
6677
- "latin",
6678
- "ea",
6679
- "cs",
6680
- "solidFill"
6722
+ var COLOR_TRANSFORM_KINDS = /* @__PURE__ */ new Set([
6723
+ "lumMod",
6724
+ "lumOff",
6725
+ "tint",
6726
+ "shade",
6727
+ "alpha"
6681
6728
  ]);
6682
- var ALIGN_MAP = {
6683
- l: "left",
6684
- ctr: "center",
6685
- r: "right",
6686
- just: "justify"
6687
- };
6688
- var ANCHOR_MAP = {
6689
- t: "top",
6690
- ctr: "middle",
6691
- b: "bottom"
6729
+ var RAW_FILL_LOCAL_NAMES = ["grpFill"];
6730
+ var PRESET_COLOR_HEX = {
6731
+ black: "000000",
6732
+ white: "FFFFFF",
6733
+ red: "FF0000",
6734
+ green: "008000",
6735
+ blue: "0000FF",
6736
+ yellow: "FFFF00",
6737
+ cyan: "00FFFF",
6738
+ magenta: "FF00FF"
6692
6739
  };
6693
- var WRAP_VALUES = /* @__PURE__ */ new Set(["square", "none"]);
6694
- var VERTICAL_VALUES = /* @__PURE__ */ new Set([
6695
- "horz",
6696
- "vert",
6697
- "vert270",
6698
- "eaVert",
6699
- "wordArtVert",
6700
- "mongolianVert"
6701
- ]);
6702
- var VALID_AUTO_NUM_SCHEMES = /* @__PURE__ */ new Set([
6703
- "arabicPeriod",
6704
- "arabicParenR",
6705
- "romanUcPeriod",
6706
- "romanLcPeriod",
6707
- "alphaUcPeriod",
6708
- "alphaLcPeriod",
6709
- "alphaLcParenR",
6710
- "alphaUcParenR",
6711
- "arabicPlain"
6712
- ]);
6713
- function parseTextBody(txBody, partPath, nextId, ownerNodeId, ownerOrderingSlot, orderedTxBody) {
6714
- if (!txBody) return void 0;
6715
- const properties = parseBodyProperties(getChild(txBody, "bodyPr"));
6716
- const listStyle = parseTextStyle(getChild(txBody, "lstStyle"));
6717
- const orderedParagraphs = orderedTxBody?.filter((child) => orderedLocalName(child) === "p").map((child) => {
6718
- const key = orderedKey(child);
6719
- const value = key !== void 0 ? child[key] : void 0;
6720
- return Array.isArray(value) ? value : void 0;
6721
- });
6722
- const paragraphs = [];
6723
- let logicalParagraphIndex = 0;
6724
- getChildArray(txBody, "p").forEach((p, paragraphIndex) => {
6725
- const orderedChildren = orderedParagraphs?.[paragraphIndex];
6726
- if (orderedChildren !== void 0 && hasMultipleBulletPPr(p, orderedChildren)) {
6727
- const split = splitInterleavedParagraph(
6728
- p,
6729
- orderedChildren,
6730
- partPath,
6731
- nextId,
6732
- ownerNodeId,
6733
- ownerOrderingSlot,
6734
- logicalParagraphIndex
6740
+ function parseColorElement(parent) {
6741
+ if (!parent) return void 0;
6742
+ const srgb = getChild(parent, "srgbClr");
6743
+ if (srgb) {
6744
+ const hex = getAttr(srgb, "val");
6745
+ if (hex !== void 0) {
6746
+ return withTransforms({ kind: "srgb", hex: hex.toUpperCase() }, srgb);
6747
+ }
6748
+ }
6749
+ const scheme = getChild(parent, "schemeClr");
6750
+ if (scheme) {
6751
+ const name = getAttr(scheme, "val");
6752
+ if (name !== void 0) {
6753
+ return withTransforms({ kind: "scheme", scheme: name }, scheme);
6754
+ }
6755
+ }
6756
+ const sys = getChild(parent, "sysClr");
6757
+ if (sys) {
6758
+ const val = getAttr(sys, "val");
6759
+ if (val !== void 0) {
6760
+ const lastClr = getAttr(sys, "lastClr");
6761
+ return withTransforms(
6762
+ {
6763
+ kind: "system",
6764
+ value: val,
6765
+ ...lastClr !== void 0 ? { lastColor: lastClr.toUpperCase() } : {}
6766
+ },
6767
+ sys
6735
6768
  );
6736
- paragraphs.push(...split);
6737
- logicalParagraphIndex += split.length;
6738
- return;
6739
6769
  }
6740
- paragraphs.push(
6741
- parseParagraph(
6742
- p,
6743
- partPath,
6744
- nextId,
6745
- ownerNodeId,
6746
- ownerOrderingSlot,
6747
- logicalParagraphIndex,
6748
- orderedChildren
6749
- )
6750
- );
6751
- logicalParagraphIndex++;
6752
- });
6753
- const rawSidecars = collectUnknownSidecars(txBody, KNOWN_TXBODY_CHILDREN, nextId);
6754
- return {
6755
- paragraphs,
6756
- ...properties !== void 0 ? { properties } : {},
6757
- ...listStyle !== void 0 ? { listStyle } : {},
6758
- handle: { partPath },
6759
- ...rawSidecars.length > 0 ? { rawSidecars } : {}
6770
+ }
6771
+ return void 0;
6772
+ }
6773
+ function parseEffectList(effectList) {
6774
+ if (effectList === void 0) return void 0;
6775
+ const outerShadow = parseOuterShadow(getChild(effectList, "outerShdw"));
6776
+ const innerShadow = parseInnerShadow(getChild(effectList, "innerShdw"));
6777
+ const glow = parseGlow(getChild(effectList, "glow"));
6778
+ const softEdge = parseSoftEdge(getChild(effectList, "softEdge"));
6779
+ const parsed = {
6780
+ ...outerShadow !== void 0 ? { outerShadow } : {},
6781
+ ...innerShadow !== void 0 ? { innerShadow } : {},
6782
+ ...glow !== void 0 ? { glow } : {},
6783
+ ...softEdge !== void 0 ? { softEdge } : {}
6784
+ };
6785
+ return Object.keys(parsed).length > 0 ? parsed : void 0;
6786
+ }
6787
+ function parseBlipEffects(blip) {
6788
+ if (blip === void 0) return void 0;
6789
+ const grayscale = getChild(blip, "grayscl") !== void 0;
6790
+ const biLevel = parseBiLevel(getChild(blip, "biLevel"));
6791
+ const blur = parseBlurEffect(getChild(blip, "blur"));
6792
+ const lum = parseLumEffect(getChild(blip, "lum"));
6793
+ const duotone = parseDuotoneEffect(getChild(blip, "duotone"));
6794
+ const clrChange = parseColorChangeEffect(getChild(blip, "clrChange"));
6795
+ const parsed = {
6796
+ grayscale,
6797
+ ...biLevel !== void 0 ? { biLevel } : {},
6798
+ ...blur !== void 0 ? { blur } : {},
6799
+ ...lum !== void 0 ? { lum } : {},
6800
+ ...duotone !== void 0 ? { duotone } : {},
6801
+ ...clrChange !== void 0 ? { clrChange } : {}
6760
6802
  };
6803
+ return grayscale || biLevel !== void 0 || blur !== void 0 || lum !== void 0 || duotone !== void 0 || clrChange !== void 0 ? parsed : void 0;
6761
6804
  }
6762
- function parseTextStyle(node) {
6805
+ function parseOuterShadow(node) {
6763
6806
  if (node === void 0) return void 0;
6764
- const defaultParagraph = parseParagraphProperties(getChild(node, "defPPr"));
6765
- const levels = Array.from(
6766
- { length: 9 },
6767
- (_, index) => parseParagraphProperties(getChild(node, `lvl${index + 1}pPr`))
6768
- );
6769
- if (defaultParagraph === void 0 && levels.every((level) => level === void 0)) {
6770
- return void 0;
6771
- }
6772
- return { ...defaultParagraph !== void 0 ? { defaultParagraph } : {}, levels };
6807
+ const color = parseColorElement(node);
6808
+ if (color === void 0) return void 0;
6809
+ return {
6810
+ blurRadius: asEmu2(numericAttr2(node, "blurRad") ?? 0),
6811
+ distance: asEmu2(numericAttr2(node, "dist") ?? 0),
6812
+ direction: asOoxmlAngle(numericAttr2(node, "dir") ?? 0),
6813
+ color,
6814
+ alignment: parseRectangleAlignment(getAttr(node, "algn"), "b"),
6815
+ rotateWithShape: getAttr(node, "rotWithShape") !== "0"
6816
+ };
6773
6817
  }
6774
- function hasMultipleBulletPPr(p, orderedChildren) {
6775
- const pPrList = getChildArray(p, "pPr");
6776
- let bulletPPrCount = 0;
6777
- let pPrCounter = 0;
6778
- for (const child of orderedChildren) {
6779
- if (orderedLocalName(child) !== "pPr") continue;
6780
- const pPr = pPrList[pPrCounter];
6781
- if (pPr !== void 0 && (getChild(pPr, "buChar") !== void 0 || getChild(pPr, "buAutoNum") !== void 0)) {
6782
- bulletPPrCount++;
6783
- if (bulletPPrCount >= 2) return true;
6784
- }
6785
- pPrCounter++;
6786
- }
6787
- return false;
6818
+ function parseInnerShadow(node) {
6819
+ if (node === void 0) return void 0;
6820
+ const color = parseColorElement(node);
6821
+ if (color === void 0) return void 0;
6822
+ return {
6823
+ blurRadius: asEmu2(numericAttr2(node, "blurRad") ?? 0),
6824
+ distance: asEmu2(numericAttr2(node, "dist") ?? 0),
6825
+ direction: asOoxmlAngle(numericAttr2(node, "dir") ?? 0),
6826
+ color
6827
+ };
6788
6828
  }
6789
- function splitInterleavedParagraph(p, orderedChildren, partPath, nextId, ownerNodeId, ownerOrderingSlot, paragraphIndex) {
6829
+ function parseGlow(node) {
6830
+ if (node === void 0) return void 0;
6831
+ const color = parseColorElement(node);
6832
+ if (color === void 0) return void 0;
6833
+ return {
6834
+ radius: asEmu2(numericAttr2(node, "rad") ?? 0),
6835
+ color
6836
+ };
6837
+ }
6838
+ function parseSoftEdge(node) {
6839
+ if (node === void 0) return void 0;
6840
+ return {
6841
+ radius: asEmu2(numericAttr2(node, "rad") ?? 0)
6842
+ };
6843
+ }
6844
+ function parseBiLevel(node) {
6845
+ if (node === void 0) return void 0;
6846
+ return {
6847
+ threshold: (numericAttr2(node, "thresh") ?? 5e4) / 1e5
6848
+ };
6849
+ }
6850
+ function parseBlurEffect(node) {
6851
+ if (node === void 0) return void 0;
6852
+ return {
6853
+ radius: asEmu2(numericAttr2(node, "rad") ?? 0),
6854
+ grow: getAttr(node, "grow") !== "0"
6855
+ };
6856
+ }
6857
+ function parseLumEffect(node) {
6858
+ if (node === void 0) return void 0;
6859
+ return {
6860
+ brightness: (numericAttr2(node, "bright") ?? 0) / 1e5,
6861
+ contrast: (numericAttr2(node, "contrast") ?? 0) / 1e5
6862
+ };
6863
+ }
6864
+ function parseDuotoneEffect(node) {
6865
+ if (node === void 0) return void 0;
6866
+ const colors = collectColorChildren(node);
6867
+ if (colors.length < 2) return void 0;
6868
+ return { color1: colors[0], color2: colors[1] };
6869
+ }
6870
+ function parseColorChangeEffect(node) {
6871
+ if (node === void 0) return void 0;
6872
+ const from = firstColorChild(getChild(node, "clrFrom"));
6873
+ const to = firstColorChild(getChild(node, "clrTo"));
6874
+ return from !== void 0 && to !== void 0 ? { from, to } : void 0;
6875
+ }
6876
+ function collectColorChildren(parent) {
6877
+ const colors = [];
6878
+ for (const [key, value] of Object.entries(parent)) {
6879
+ if (key.startsWith("@_")) continue;
6880
+ const nodes = Array.isArray(value) ? value : [value];
6881
+ for (const node of nodes) {
6882
+ if (!isXmlNode(node)) continue;
6883
+ const color = parseColorChild(key, node);
6884
+ if (color !== void 0) colors.push(color);
6885
+ }
6886
+ }
6887
+ return colors;
6888
+ }
6889
+ function firstColorChild(parent) {
6890
+ return parent !== void 0 ? collectColorChildren(parent)[0] : void 0;
6891
+ }
6892
+ function parseColorChild(key, node) {
6893
+ const name = localName(key);
6894
+ return name === "prstClr" ? parsePresetColor(node) : parseColorElement({ [name]: node });
6895
+ }
6896
+ function isXmlNode(value) {
6897
+ return typeof value === "object" && value !== null;
6898
+ }
6899
+ function parsePresetColor(node) {
6900
+ const value = getAttr(node, "val");
6901
+ const hex = value !== void 0 ? PRESET_COLOR_HEX[value] : void 0;
6902
+ return hex !== void 0 ? withTransforms({ kind: "srgb", hex }, node) : void 0;
6903
+ }
6904
+ function parseFill(parent, nextId) {
6905
+ if (!parent) return void 0;
6906
+ const solid = getChild(parent, "solidFill");
6907
+ if (solid) {
6908
+ const color = parseColorElement(solid);
6909
+ if (color) return { kind: "solid", color };
6910
+ return { kind: "raw", raw: makeSidecar("a:solidFill", solid, nextId) };
6911
+ }
6912
+ if (hasChild(parent, "noFill")) return { kind: "none" };
6913
+ const grad = getChild(parent, "gradFill");
6914
+ if (grad) {
6915
+ const fill = parseGradientFill(grad);
6916
+ return fill ?? { kind: "raw", raw: makeSidecar("a:gradFill", grad, nextId) };
6917
+ }
6918
+ const blip = getChild(parent, "blipFill");
6919
+ if (blip) {
6920
+ return parseBlipFill(blip) ?? { kind: "raw", raw: makeSidecar("a:blipFill", blip, nextId) };
6921
+ }
6922
+ const pattern = getChild(parent, "pattFill");
6923
+ if (pattern) {
6924
+ const fill = parsePatternFill(pattern);
6925
+ return fill ?? { kind: "raw", raw: makeSidecar("a:pattFill", pattern, nextId) };
6926
+ }
6927
+ for (const name of RAW_FILL_LOCAL_NAMES) {
6928
+ if (hasChild(parent, name)) {
6929
+ return { kind: "raw", raw: makeSidecar(`a:${name}`, getChild(parent, name) ?? {}, nextId) };
6930
+ }
6931
+ }
6932
+ return void 0;
6933
+ }
6934
+ function parseGradientFill(grad) {
6935
+ const stops = [];
6936
+ for (const gs of getChildArray(getChild(grad, "gsLst"), "gs")) {
6937
+ const color = parseColorElement(gs);
6938
+ if (color === void 0) continue;
6939
+ stops.push({
6940
+ position: (numericAttr2(gs, "pos") ?? 0) / 1e5,
6941
+ color
6942
+ });
6943
+ }
6944
+ if (stops.length === 0) return void 0;
6945
+ const path = getChild(grad, "path");
6946
+ if (path !== void 0) {
6947
+ const fillToRect = getChild(path, "fillToRect");
6948
+ const l = numericAttr2(fillToRect, "l") ?? 0;
6949
+ const t = numericAttr2(fillToRect, "t") ?? 0;
6950
+ const r = numericAttr2(fillToRect, "r") ?? 0;
6951
+ const b = numericAttr2(fillToRect, "b") ?? 0;
6952
+ return {
6953
+ kind: "gradient",
6954
+ gradientType: "radial",
6955
+ stops,
6956
+ centerX: (l + (1e5 - r)) / 2 / 1e5,
6957
+ centerY: (t + (1e5 - b)) / 2 / 1e5
6958
+ };
6959
+ }
6960
+ return {
6961
+ kind: "gradient",
6962
+ gradientType: "linear",
6963
+ stops,
6964
+ angle: asOoxmlAngle(numericAttr2(getChild(grad, "lin"), "ang") ?? 0)
6965
+ };
6966
+ }
6967
+ function parseBlipFill(blipFill) {
6968
+ const blip = getChild(blipFill, "blip");
6969
+ const embed = getNamespacedAttr(blip, "embed");
6970
+ if (embed === void 0) return void 0;
6971
+ const tile = parseImageFillTile(getChild(blipFill, "tile"));
6972
+ return {
6973
+ kind: "image",
6974
+ blipRelationshipId: asRelationshipId(embed),
6975
+ ...tile !== void 0 ? { tile } : {}
6976
+ };
6977
+ }
6978
+ function parseImageFillTile(tile) {
6979
+ if (tile === void 0) return void 0;
6980
+ const flip = getAttr(tile, "flip") ?? "none";
6981
+ return {
6982
+ tx: asEmu2(numericAttr2(tile, "tx") ?? 0),
6983
+ ty: asEmu2(numericAttr2(tile, "ty") ?? 0),
6984
+ sx: (numericAttr2(tile, "sx") ?? 1e5) / 1e5,
6985
+ sy: (numericAttr2(tile, "sy") ?? 1e5) / 1e5,
6986
+ flip: flip === "x" || flip === "y" || flip === "xy" ? flip : "none",
6987
+ align: parseRectangleAlignment(getAttr(tile, "algn"), "tl")
6988
+ };
6989
+ }
6990
+ function parseRectangleAlignment(value, fallback) {
6991
+ return parseEnumValueWithDefault(value, RECTANGLE_ALIGNMENT_TOKENS, fallback);
6992
+ }
6993
+ function parsePatternFill(pattern) {
6994
+ const foregroundColor = parseColorElement(getChild(pattern, "fgClr"));
6995
+ const backgroundColor = parseColorElement(getChild(pattern, "bgClr"));
6996
+ if (foregroundColor === void 0 || backgroundColor === void 0) return void 0;
6997
+ return {
6998
+ kind: "pattern",
6999
+ preset: getAttr(pattern, "prst") ?? "ltDnDiag",
7000
+ foregroundColor,
7001
+ backgroundColor
7002
+ };
7003
+ }
7004
+ function parseOutline(spPr, nextId) {
7005
+ const ln = getChild(spPr, "ln");
7006
+ return parseLine(ln, nextId);
7007
+ }
7008
+ function parseLine(ln, nextId) {
7009
+ if (!ln) return void 0;
7010
+ const width = numericAttr2(ln, "w");
7011
+ const fill = parseFill(ln, nextId);
7012
+ const dashStyle = parseDashStyle(getChild(ln, "prstDash"));
7013
+ const customDash = parseCustomDash(ln);
7014
+ const lineCap = parseLineCap(getAttr(ln, "cap"));
7015
+ const lineJoin = parseLineJoin(ln);
7016
+ const headEnd = parseArrowEndpoint(getChild(ln, "headEnd"));
7017
+ const tailEnd = parseArrowEndpoint(getChild(ln, "tailEnd"));
7018
+ return {
7019
+ ...width !== void 0 ? { width: asEmu2(width) } : {},
7020
+ ...fill !== void 0 ? { fill } : {},
7021
+ ...dashStyle !== void 0 ? { dashStyle } : {},
7022
+ ...customDash !== void 0 ? { customDash } : {},
7023
+ ...lineCap !== void 0 ? { lineCap } : {},
7024
+ ...lineJoin !== void 0 ? { lineJoin } : {},
7025
+ ...headEnd !== void 0 ? { headEnd } : {},
7026
+ ...tailEnd !== void 0 ? { tailEnd } : {}
7027
+ };
7028
+ }
7029
+ function parseTransform(spPr) {
7030
+ const xfrm = getChild(spPr, "xfrm");
7031
+ if (!xfrm) return void 0;
7032
+ const off = getChild(xfrm, "off");
7033
+ const ext = getChild(xfrm, "ext");
7034
+ const offsetX = numericAttr2(off, "x");
7035
+ const offsetY = numericAttr2(off, "y");
7036
+ const width = numericAttr2(ext, "cx");
7037
+ const height = numericAttr2(ext, "cy");
7038
+ if (offsetX === void 0 || offsetY === void 0 || width === void 0 || height === void 0) {
7039
+ return void 0;
7040
+ }
7041
+ const rotation = numericAttr2(xfrm, "rot");
7042
+ const flipH = getAttr(xfrm, "flipH");
7043
+ const flipV = getAttr(xfrm, "flipV");
7044
+ return {
7045
+ offsetX: asEmu2(offsetX),
7046
+ offsetY: asEmu2(offsetY),
7047
+ width: asEmu2(width),
7048
+ height: asEmu2(height),
7049
+ ...rotation !== void 0 ? { rotation: asOoxmlAngle(rotation) } : {},
7050
+ ...isTrue(flipH) ? { flipHorizontal: true } : {},
7051
+ ...isTrue(flipV) ? { flipVertical: true } : {}
7052
+ };
7053
+ }
7054
+ function withTransforms(base, colorNode) {
7055
+ const transforms = [];
7056
+ for (const key of Object.keys(colorNode)) {
7057
+ if (key.startsWith("@_")) continue;
7058
+ const kind = localName(key);
7059
+ const transformKind = parseEnumValue(kind, COLOR_TRANSFORM_KINDS);
7060
+ if (transformKind === void 0) continue;
7061
+ const value = colorNode[key];
7062
+ const items = Array.isArray(value) ? value : [value];
7063
+ for (const item of items) {
7064
+ const raw = getAttr(unsafeOoxmlBoundaryAssertion(item), "val");
7065
+ if (raw === void 0) continue;
7066
+ const numeric = Number(raw);
7067
+ if (!Number.isFinite(numeric)) continue;
7068
+ transforms.push({
7069
+ kind: transformKind,
7070
+ value: asOoxmlPercent(numeric)
7071
+ });
7072
+ }
7073
+ }
7074
+ return transforms.length > 0 ? { ...base, transforms } : base;
7075
+ }
7076
+ function numericAttr2(node, name) {
7077
+ const raw = getAttr(node, name);
7078
+ if (raw === void 0) return void 0;
7079
+ const value = Number(raw);
7080
+ return Number.isFinite(value) ? value : void 0;
7081
+ }
7082
+ function isTrue(value) {
7083
+ return value === "1" || value === "true";
7084
+ }
7085
+ function parseDashStyle(prstDash) {
7086
+ const value = getAttr(prstDash, "val");
7087
+ return parseEnumValue(value, DASH_STYLES);
7088
+ }
7089
+ function parseCustomDash(ln) {
7090
+ const custDash = getChild(ln, "custDash");
7091
+ const segments = getChildArray(custDash, "ds");
7092
+ if (segments.length === 0) return void 0;
7093
+ return segments.flatMap((segment) => [
7094
+ (numericAttr2(segment, "d") ?? 1e5) / 1e5,
7095
+ (numericAttr2(segment, "sp") ?? 1e5) / 1e5
7096
+ ]);
7097
+ }
7098
+ var LINE_CAP_MAP = {
7099
+ flat: "butt",
7100
+ sq: "square",
7101
+ rnd: "round"
7102
+ };
7103
+ function parseLineCap(value) {
7104
+ return value !== void 0 ? LINE_CAP_MAP[value] : void 0;
7105
+ }
7106
+ function parseLineJoin(ln) {
7107
+ if (hasChild(ln, "round")) return "round";
7108
+ if (hasChild(ln, "bevel")) return "bevel";
7109
+ if (hasChild(ln, "miter")) return "miter";
7110
+ return void 0;
7111
+ }
7112
+ function parseArrowEndpoint(node) {
7113
+ if (!node) return void 0;
7114
+ const type = parseEnumValue(getAttr(node, "type"), ARROW_TYPES);
7115
+ if (type === void 0) return void 0;
7116
+ const width = getAttr(node, "w") ?? "med";
7117
+ const length = getAttr(node, "len") ?? "med";
7118
+ return {
7119
+ type,
7120
+ width: parseEnumValueWithDefault(width, ARROW_SIZES, "med"),
7121
+ length: parseEnumValueWithDefault(length, ARROW_SIZES, "med")
7122
+ };
7123
+ }
7124
+ var DASH_STYLES = /* @__PURE__ */ new Set([
7125
+ "solid",
7126
+ "dash",
7127
+ "dot",
7128
+ "dashDot",
7129
+ "lgDash",
7130
+ "lgDashDot",
7131
+ "sysDash",
7132
+ "sysDot"
7133
+ ]);
7134
+ var ARROW_TYPES = /* @__PURE__ */ new Set([
7135
+ "triangle",
7136
+ "stealth",
7137
+ "diamond",
7138
+ "oval",
7139
+ "arrow"
7140
+ ]);
7141
+ var ARROW_SIZES = /* @__PURE__ */ new Set(["sm", "med", "lg"]);
7142
+ var KNOWN_TXBODY_CHILDREN = /* @__PURE__ */ new Set(["bodyPr", "lstStyle", "p"]);
7143
+ var KNOWN_PARAGRAPH_CHILDREN = /* @__PURE__ */ new Set([
7144
+ "pPr",
7145
+ "r",
7146
+ "fld",
7147
+ "br",
7148
+ "endParaRPr"
7149
+ ]);
7150
+ var KNOWN_RUN_CHILDREN = /* @__PURE__ */ new Set(["rPr", "t"]);
7151
+ var KNOWN_RUN_PROPERTY_CHILDREN = /* @__PURE__ */ new Set([
7152
+ "latin",
7153
+ "ea",
7154
+ "cs",
7155
+ "solidFill"
7156
+ ]);
7157
+ var ALIGN_MAP = {
7158
+ l: "left",
7159
+ ctr: "center",
7160
+ r: "right",
7161
+ just: "justify"
7162
+ };
7163
+ var ANCHOR_MAP = {
7164
+ t: "top",
7165
+ ctr: "middle",
7166
+ b: "bottom"
7167
+ };
7168
+ var WRAP_VALUES = /* @__PURE__ */ new Set(["square", "none"]);
7169
+ var VERTICAL_VALUES = /* @__PURE__ */ new Set([
7170
+ "horz",
7171
+ "vert",
7172
+ "vert270",
7173
+ "eaVert",
7174
+ "wordArtVert",
7175
+ "mongolianVert"
7176
+ ]);
7177
+ var VALID_AUTO_NUM_SCHEMES = /* @__PURE__ */ new Set([
7178
+ "arabicPeriod",
7179
+ "arabicParenR",
7180
+ "romanUcPeriod",
7181
+ "romanLcPeriod",
7182
+ "alphaUcPeriod",
7183
+ "alphaLcPeriod",
7184
+ "alphaLcParenR",
7185
+ "alphaUcParenR",
7186
+ "arabicPlain"
7187
+ ]);
7188
+ function parseTextBody(txBody, partPath, nextId, ownerNodeId, ownerOrderingSlot, orderedTxBody) {
7189
+ if (!txBody) return void 0;
7190
+ const properties = parseBodyProperties(getChild(txBody, "bodyPr"));
7191
+ const listStyle = parseTextStyle(getChild(txBody, "lstStyle"));
7192
+ const orderedParagraphs = orderedTxBody?.filter((child) => orderedLocalName(child) === "p").map((child) => {
7193
+ const key = orderedKey(child);
7194
+ const value = key !== void 0 ? child[key] : void 0;
7195
+ return Array.isArray(value) ? value : void 0;
7196
+ });
7197
+ const paragraphs = [];
7198
+ let logicalParagraphIndex = 0;
7199
+ getChildArray(txBody, "p").forEach((p, paragraphIndex) => {
7200
+ const orderedChildren = orderedParagraphs?.[paragraphIndex];
7201
+ if (orderedChildren !== void 0 && hasMultipleBulletPPr(p, orderedChildren)) {
7202
+ const split = splitInterleavedParagraph(
7203
+ p,
7204
+ orderedChildren,
7205
+ partPath,
7206
+ nextId,
7207
+ ownerNodeId,
7208
+ ownerOrderingSlot,
7209
+ logicalParagraphIndex
7210
+ );
7211
+ paragraphs.push(...split);
7212
+ logicalParagraphIndex += split.length;
7213
+ return;
7214
+ }
7215
+ paragraphs.push(
7216
+ parseParagraph(
7217
+ p,
7218
+ partPath,
7219
+ nextId,
7220
+ ownerNodeId,
7221
+ ownerOrderingSlot,
7222
+ logicalParagraphIndex,
7223
+ orderedChildren
7224
+ )
7225
+ );
7226
+ logicalParagraphIndex++;
7227
+ });
7228
+ const rawSidecars = collectUnknownSidecars(txBody, KNOWN_TXBODY_CHILDREN, nextId);
7229
+ return {
7230
+ paragraphs,
7231
+ ...properties !== void 0 ? { properties } : {},
7232
+ ...listStyle !== void 0 ? { listStyle } : {},
7233
+ handle: { partPath },
7234
+ ...rawSidecars.length > 0 ? { rawSidecars } : {}
7235
+ };
7236
+ }
7237
+ function parseTextStyle(node) {
7238
+ if (node === void 0) return void 0;
7239
+ const defaultParagraph = parseParagraphProperties(getChild(node, "defPPr"));
7240
+ const levels = Array.from(
7241
+ { length: 9 },
7242
+ (_, index) => parseParagraphProperties(getChild(node, `lvl${index + 1}pPr`))
7243
+ );
7244
+ if (defaultParagraph === void 0 && levels.every((level) => level === void 0)) {
7245
+ return void 0;
7246
+ }
7247
+ return { ...defaultParagraph !== void 0 ? { defaultParagraph } : {}, levels };
7248
+ }
7249
+ function hasMultipleBulletPPr(p, orderedChildren) {
7250
+ const pPrList = getChildArray(p, "pPr");
7251
+ let bulletPPrCount = 0;
7252
+ let pPrCounter = 0;
7253
+ for (const child of orderedChildren) {
7254
+ if (orderedLocalName(child) !== "pPr") continue;
7255
+ const pPr = pPrList[pPrCounter];
7256
+ if (pPr !== void 0 && (getChild(pPr, "buChar") !== void 0 || getChild(pPr, "buAutoNum") !== void 0)) {
7257
+ bulletPPrCount++;
7258
+ if (bulletPPrCount >= 2) return true;
7259
+ }
7260
+ pPrCounter++;
7261
+ }
7262
+ return false;
7263
+ }
7264
+ function splitInterleavedParagraph(p, orderedChildren, partPath, nextId, ownerNodeId, ownerOrderingSlot, paragraphIndex) {
6790
7265
  const pPrList = getChildArray(p, "pPr");
6791
7266
  const rList = getChildArray(p, "r");
6792
7267
  const fldList = getChildArray(p, "fld");
@@ -7919,6 +8394,7 @@ var xmlBuilder = new import_fast_xml_parser2.XMLBuilder({
7919
8394
  suppressEmptyNode: true
7920
8395
  });
7921
8396
  function buildTextBoxXml(params) {
8397
+ const paragraphs = params.paragraphs ?? [{ runs: [{ text: params.text ?? "" }] }];
7922
8398
  return xmlBuilder.build({
7923
8399
  "p:sp": {
7924
8400
  "p:nvSpPr": {
@@ -7933,6 +8409,7 @@ function buildTextBoxXml(params) {
7933
8409
  },
7934
8410
  "p:spPr": {
7935
8411
  "a:xfrm": {
8412
+ ...params.rotation !== void 0 ? { "@_rot": String(params.rotation) } : {},
7936
8413
  "a:off": {
7937
8414
  "@_x": String(params.offsetX),
7938
8415
  "@_y": String(params.offsetY)
@@ -7952,20 +8429,142 @@ function buildTextBoxXml(params) {
7952
8429
  }
7953
8430
  },
7954
8431
  "p:txBody": {
7955
- "a:bodyPr": {
7956
- "@_wrap": "square"
7957
- },
8432
+ "a:bodyPr": createTextBodyPropertiesXml(params.body),
7958
8433
  "a:lstStyle": {},
7959
- "a:p": {
7960
- "a:r": {
7961
- "a:t": textElementValue(params.text)
7962
- },
7963
- "a:endParaRPr": {}
7964
- }
8434
+ "a:p": paragraphs.map(createParagraphXml)
7965
8435
  }
7966
8436
  }
7967
8437
  });
7968
8438
  }
8439
+ function createTextBodyPropertiesXml(body) {
8440
+ return {
8441
+ "@_wrap": "square",
8442
+ ...body?.anchor !== void 0 ? { "@_anchor": verticalAnchorToken(body.anchor) } : {},
8443
+ ...body?.marginLeft !== void 0 ? { "@_lIns": String(body.marginLeft) } : {},
8444
+ ...body?.marginRight !== void 0 ? { "@_rIns": String(body.marginRight) } : {},
8445
+ ...body?.marginTop !== void 0 ? { "@_tIns": String(body.marginTop) } : {},
8446
+ ...body?.marginBottom !== void 0 ? { "@_bIns": String(body.marginBottom) } : {}
8447
+ };
8448
+ }
8449
+ function createParagraphXml(paragraph) {
8450
+ return {
8451
+ ...paragraph.properties !== void 0 ? { "a:pPr": createParagraphPropertiesXml(paragraph.properties) } : {},
8452
+ "a:r": paragraph.runs.map(createTextRunXml),
8453
+ "a:endParaRPr": {}
8454
+ };
8455
+ }
8456
+ function createParagraphPropertiesXml(properties) {
8457
+ return {
8458
+ ...properties.align !== void 0 ? { "@_algn": textAlignToken(properties.align) } : {},
8459
+ ...properties.lineSpacing !== void 0 ? { "a:lnSpc": { "a:spcPts": { "@_val": String(properties.lineSpacing) } } } : {}
8460
+ };
8461
+ }
8462
+ function createTextRunXml(run) {
8463
+ return {
8464
+ ...run.properties !== void 0 ? { "a:rPr": createTextRunPropertiesXml(run.properties) } : {},
8465
+ "a:t": textElementValue(run.text)
8466
+ };
8467
+ }
8468
+ function createTextRunPropertiesXml(properties) {
8469
+ return {
8470
+ ...properties.bold !== void 0 ? { "@_b": boolToken(properties.bold) } : {},
8471
+ ...properties.italic !== void 0 ? { "@_i": boolToken(properties.italic) } : {},
8472
+ ...properties.underline !== void 0 ? { "@_u": underlineStyleToken(properties.underline) } : {},
8473
+ ...properties.strike !== void 0 ? { "@_strike": properties.strike ? "sngStrike" : "noStrike" } : {},
8474
+ ...properties.baseline !== void 0 ? { "@_baseline": String(baselineToken(properties.baseline)) } : {},
8475
+ ...properties.fontSize !== void 0 ? { "@_sz": String(Math.round(properties.fontSize * 100)) } : {},
8476
+ ...properties.charSpacing !== void 0 ? { "@_spc": textPointToken(properties.charSpacing) } : {},
8477
+ ...properties.outline !== void 0 ? { "a:ln": createTextOutlineXml(properties.outline) } : {},
8478
+ ...properties.color !== void 0 ? { "a:solidFill": createSolidFillXml(properties.color) } : {},
8479
+ ...properties.gradientFill !== void 0 ? { "a:gradFill": createGradientFillXml(properties.gradientFill) } : {},
8480
+ ...properties.glow !== void 0 ? { "a:effectLst": { "a:glow": createGlowXml(properties.glow) } } : {},
8481
+ ...properties.highlight !== void 0 ? { "a:highlight": createColorXml(properties.highlight) } : {},
8482
+ ...properties.underline !== void 0 && typeof properties.underline !== "boolean" && properties.underline.color !== void 0 ? { "a:uFill": { "a:solidFill": createSolidFillXml(properties.underline.color) } } : {},
8483
+ ...properties.fontFace !== void 0 ? {
8484
+ "a:latin": { "@_typeface": properties.fontFace },
8485
+ "a:ea": { "@_typeface": properties.fontFace },
8486
+ "a:cs": { "@_typeface": properties.fontFace }
8487
+ } : {}
8488
+ };
8489
+ }
8490
+ function createSolidFillXml(color) {
8491
+ return createColorXml(color);
8492
+ }
8493
+ function createColorXml(color) {
8494
+ if (!/^[0-9A-Fa-f]{6}$/.test(color.hex)) {
8495
+ throw new Error("buildTextBoxXml: color hex must be a 6-digit RGB value");
8496
+ }
8497
+ return {
8498
+ "a:srgbClr": {
8499
+ "@_val": color.hex.toUpperCase()
8500
+ }
8501
+ };
8502
+ }
8503
+ function createGradientFillXml(fill) {
8504
+ return {
8505
+ "a:gsLst": {
8506
+ "a:gs": fill.stops.map((stop) => ({
8507
+ "@_pos": String(stop.position),
8508
+ ...createColorXml(stop.color)
8509
+ }))
8510
+ },
8511
+ "a:lin": {
8512
+ "@_ang": String(fill.angle ?? 0),
8513
+ "@_scaled": "1"
8514
+ }
8515
+ };
8516
+ }
8517
+ function createTextOutlineXml(outline) {
8518
+ return {
8519
+ ...outline.width !== void 0 ? { "@_w": String(outline.width) } : {},
8520
+ ...outline.color !== void 0 ? { "a:solidFill": createSolidFillXml(outline.color) } : {}
8521
+ };
8522
+ }
8523
+ function createGlowXml(glow) {
8524
+ return {
8525
+ "@_rad": String(glow.radius),
8526
+ ...createColorXml(glow.color)
8527
+ };
8528
+ }
8529
+ function underlineStyleToken(underline) {
8530
+ if (typeof underline === "boolean") return underline ? "sng" : "none";
8531
+ return underline.style ?? "sng";
8532
+ }
8533
+ function baselineToken(baseline) {
8534
+ if (baseline === "superscript") return 3e4;
8535
+ return -25e3;
8536
+ }
8537
+ function textPointToken(value) {
8538
+ if (!Number.isInteger(value) || value < -4e5 || value > 4e5) {
8539
+ throw new Error("buildTextBoxXml: charSpacing must be an integer between -400000 and 400000");
8540
+ }
8541
+ return String(value);
8542
+ }
8543
+ function boolToken(value) {
8544
+ return value ? "1" : "0";
8545
+ }
8546
+ function textAlignToken(align) {
8547
+ switch (align) {
8548
+ case "left":
8549
+ return "l";
8550
+ case "center":
8551
+ return "ctr";
8552
+ case "right":
8553
+ return "r";
8554
+ case "justify":
8555
+ return "just";
8556
+ }
8557
+ }
8558
+ function verticalAnchorToken(anchor) {
8559
+ switch (anchor) {
8560
+ case "top":
8561
+ return "t";
8562
+ case "middle":
8563
+ return "ctr";
8564
+ case "bottom":
8565
+ return "b";
8566
+ }
8567
+ }
7969
8568
  function buildConnectorXml(params) {
7970
8569
  return xmlBuilder.build({
7971
8570
  "p:cxnSp": {
@@ -7974,16 +8573,7 @@ function buildConnectorXml(params) {
7974
8573
  "@_id": params.shapeId,
7975
8574
  "@_name": params.name
7976
8575
  },
7977
- "p:cNvCxnSpPr": {
7978
- "a:stCxn": {
7979
- "@_id": params.startShapeId,
7980
- "@_idx": String(params.startConnectionSiteIndex)
7981
- },
7982
- "a:endCxn": {
7983
- "@_id": params.endShapeId,
7984
- "@_idx": String(params.endConnectionSiteIndex)
7985
- }
7986
- },
8576
+ "p:cNvCxnSpPr": createConnectorConnectionXml(params),
7987
8577
  "p:nvPr": {}
7988
8578
  },
7989
8579
  "p:spPr": {
@@ -8006,6 +8596,22 @@ function buildConnectorXml(params) {
8006
8596
  }
8007
8597
  });
8008
8598
  }
8599
+ function createConnectorConnectionXml(params) {
8600
+ return {
8601
+ ...params.startShapeId !== void 0 && params.startConnectionSiteIndex !== void 0 ? {
8602
+ "a:stCxn": {
8603
+ "@_id": params.startShapeId,
8604
+ "@_idx": String(params.startConnectionSiteIndex)
8605
+ }
8606
+ } : {},
8607
+ ...params.endShapeId !== void 0 && params.endConnectionSiteIndex !== void 0 ? {
8608
+ "a:endCxn": {
8609
+ "@_id": params.endShapeId,
8610
+ "@_idx": String(params.endConnectionSiteIndex)
8611
+ }
8612
+ } : {}
8613
+ };
8614
+ }
8009
8615
  function createConnectorLineXml(params) {
8010
8616
  return {
8011
8617
  "a:solidFill": {
@@ -8045,8 +8651,27 @@ var CONNECTOR_PRESETS = /* @__PURE__ */ new Set([
8045
8651
  "bentConnector3",
8046
8652
  "curvedConnector3"
8047
8653
  ]);
8048
- var ARROW_TYPES = /* @__PURE__ */ new Set(["triangle", "stealth", "diamond", "oval", "arrow"]);
8049
- var ARROW_SIZES = /* @__PURE__ */ new Set(["sm", "med", "lg"]);
8654
+ var ARROW_TYPES2 = /* @__PURE__ */ new Set(["triangle", "stealth", "diamond", "oval", "arrow"]);
8655
+ var ARROW_SIZES2 = /* @__PURE__ */ new Set(["sm", "med", "lg"]);
8656
+ var UNDERLINE_STYLES = /* @__PURE__ */ new Set([
8657
+ "sng",
8658
+ "dbl",
8659
+ "heavy",
8660
+ "dotted",
8661
+ "dottedHeavy",
8662
+ "dash",
8663
+ "dashHeavy",
8664
+ "dashLong",
8665
+ "dashLongHeavy",
8666
+ "dotDash",
8667
+ "dotDashHeavy",
8668
+ "dotDotDash",
8669
+ "dotDotDashHeavy",
8670
+ "wavy",
8671
+ "wavyHeavy",
8672
+ "wavyDbl",
8673
+ "none"
8674
+ ]);
8050
8675
  function findShapeNodeBySourceHandle(source, handle) {
8051
8676
  for (const slide of source.slides) {
8052
8677
  const shape = findShapeNodeInTree(slide.shapes, handle);
@@ -8110,6 +8735,92 @@ function updateShapeTransform(source, handle, transform) {
8110
8735
  ]
8111
8736
  };
8112
8737
  }
8738
+ function setShapeFill(source, handle, fill) {
8739
+ assertEditableShapeFill(fill, "setShapeFill");
8740
+ if (handle.nodeId === void 0) {
8741
+ throw new Error("setShapeFill: shape fill edit requires a node id");
8742
+ }
8743
+ let matched = false;
8744
+ let changed = false;
8745
+ const slides = source.slides.map((slide) => {
8746
+ let slideChanged = false;
8747
+ const shapes = slide.shapes.map((shape) => {
8748
+ if (!sourceHandlesEqual(shape.handle, handle)) return shape;
8749
+ matched = true;
8750
+ if (shape.kind !== "shape") {
8751
+ throw new Error("setShapeFill: only top-level sp shapes support fill edits");
8752
+ }
8753
+ if (hasAlternateContentSidecar(shape)) {
8754
+ throw new Error("setShapeFill: shapes inside AlternateContent are not supported");
8755
+ }
8756
+ const nextFill = toSourceFill(fill);
8757
+ if (sourceFillEqual(shape.fill, nextFill)) return shape;
8758
+ changed = true;
8759
+ slideChanged = true;
8760
+ return {
8761
+ ...shape,
8762
+ fill: nextFill
8763
+ };
8764
+ });
8765
+ return slideChanged ? { ...slide, shapes } : slide;
8766
+ });
8767
+ if (!matched) {
8768
+ if (source.slides.some((slide) => hasNestedShapeNodeWithHandle(slide.shapes, handle))) {
8769
+ throw new Error("setShapeFill: nested group shape editing is not supported");
8770
+ }
8771
+ throw new Error("setShapeFill: shape handle was not found in PptxSourceModel source");
8772
+ }
8773
+ if (!changed) return source;
8774
+ return {
8775
+ ...source,
8776
+ slides,
8777
+ edits: appendShapeFillEdit(source.edits ?? [], handle, fill)
8778
+ };
8779
+ }
8780
+ function setShapeOutline(source, handle, outline) {
8781
+ assertEditableShapeOutline(outline, "setShapeOutline");
8782
+ if (handle.nodeId === void 0) {
8783
+ throw new Error("setShapeOutline: shape outline edit requires a node id");
8784
+ }
8785
+ let matched = false;
8786
+ let changed = false;
8787
+ const slides = source.slides.map((slide) => {
8788
+ let slideChanged = false;
8789
+ const shapes = slide.shapes.map((shape) => {
8790
+ if (!sourceHandlesEqual(shape.handle, handle)) return shape;
8791
+ matched = true;
8792
+ if (shape.kind !== "shape" && shape.kind !== "connector") {
8793
+ throw new Error(
8794
+ "setShapeOutline: only top-level sp and cxnSp shapes support outline edits"
8795
+ );
8796
+ }
8797
+ if (hasAlternateContentSidecar(shape)) {
8798
+ throw new Error("setShapeOutline: shapes inside AlternateContent are not supported");
8799
+ }
8800
+ const nextOutline = patchSourceOutline(shape.outline, outline);
8801
+ if (sourceOutlineEqual(shape.outline, nextOutline)) return shape;
8802
+ changed = true;
8803
+ slideChanged = true;
8804
+ return {
8805
+ ...shape,
8806
+ outline: nextOutline
8807
+ };
8808
+ });
8809
+ return slideChanged ? { ...slide, shapes } : slide;
8810
+ });
8811
+ if (!matched) {
8812
+ if (source.slides.some((slide) => hasNestedShapeNodeWithHandle(slide.shapes, handle))) {
8813
+ throw new Error("setShapeOutline: nested group shape editing is not supported");
8814
+ }
8815
+ throw new Error("setShapeOutline: shape handle was not found in PptxSourceModel source");
8816
+ }
8817
+ if (!changed) return source;
8818
+ return {
8819
+ ...source,
8820
+ slides,
8821
+ edits: appendShapeOutlineEdit(source.edits ?? [], handle, outline)
8822
+ };
8823
+ }
8113
8824
  function addTextBox(source, slideHandle, input) {
8114
8825
  assertTextBoxInput(input);
8115
8826
  const slideIndex = source.slides.findIndex(
@@ -8130,7 +8841,9 @@ function addTextBox(source, slideHandle, input) {
8130
8841
  offsetY: input.offsetY,
8131
8842
  width: input.width,
8132
8843
  height: input.height,
8133
- text: input.text
8844
+ ...input.rotation !== void 0 ? { rotation: input.rotation } : {},
8845
+ ...input.paragraphs !== void 0 ? { paragraphs: input.paragraphs } : { text: input.text },
8846
+ ...input.body !== void 0 ? { body: input.body } : {}
8134
8847
  });
8135
8848
  const shape = parseShapeNodeXml(xml, slide.partPath, orderingSlot);
8136
8849
  const slides = source.slides.map(
@@ -8162,14 +8875,14 @@ function addConnector(source, slideHandle, input) {
8162
8875
  throw new Error("addConnector: slide handle was not found in PptxSourceModel source");
8163
8876
  }
8164
8877
  const slide = source.slides[slideIndex];
8165
- const startShape = requireConnectorTargetShape(slide, input.start.shapeHandle, "start");
8166
- const endShape = requireConnectorTargetShape(slide, input.end.shapeHandle, "end");
8878
+ const startShape = input.start !== void 0 ? requireConnectorTargetShape(slide, input.start.shapeHandle, "start") : void 0;
8879
+ const endShape = input.end !== void 0 ? requireConnectorTargetShape(slide, input.end.shapeHandle, "end") : void 0;
8167
8880
  const shapeId = nextShapeId(slide.shapes, source.edits ?? [], slide.partPath);
8168
8881
  const shapeIdValue = String(shapeId);
8169
8882
  const name = input.name?.trim() || `Connector ${shapeIdValue}`;
8170
8883
  const orderingSlot = nextOrderingSlot(slide.shapes);
8171
- const startShapeId = String(startShape.nodeId);
8172
- const endShapeId = String(endShape.nodeId);
8884
+ const startShapeId = startShape !== void 0 ? String(startShape.nodeId) : void 0;
8885
+ const endShapeId = endShape !== void 0 ? String(endShape.nodeId) : void 0;
8173
8886
  const xml = buildConnectorXml({
8174
8887
  shapeId: shapeIdValue,
8175
8888
  name,
@@ -8178,10 +8891,14 @@ function addConnector(source, slideHandle, input) {
8178
8891
  offsetY: input.offsetY,
8179
8892
  width: input.width,
8180
8893
  height: input.height,
8181
- startShapeId,
8182
- startConnectionSiteIndex: input.start.connectionSiteIndex,
8183
- endShapeId,
8184
- endConnectionSiteIndex: input.end.connectionSiteIndex,
8894
+ ...startShapeId !== void 0 && input.start !== void 0 ? {
8895
+ startShapeId,
8896
+ startConnectionSiteIndex: input.start.connectionSiteIndex
8897
+ } : {},
8898
+ ...endShapeId !== void 0 && input.end !== void 0 ? {
8899
+ endShapeId,
8900
+ endConnectionSiteIndex: input.end.connectionSiteIndex
8901
+ } : {},
8185
8902
  ...input.outline !== void 0 ? { outline: input.outline } : {}
8186
8903
  });
8187
8904
  const connector = parseShapeNodeXml(xml, slide.partPath, orderingSlot);
@@ -8200,8 +8917,8 @@ function addConnector(source, slideHandle, input) {
8200
8917
  kind: "addConnector",
8201
8918
  slidePartPath: slide.partPath,
8202
8919
  shapeId: shapeIdValue,
8203
- startShapeId,
8204
- endShapeId,
8920
+ ...startShapeId !== void 0 ? { startShapeId } : {},
8921
+ ...endShapeId !== void 0 ? { endShapeId } : {},
8205
8922
  xml
8206
8923
  }
8207
8924
  ]
@@ -8218,8 +8935,8 @@ function deleteShape(source, handle) {
8218
8935
  const nextShapes = slide.shapes.filter((shape) => {
8219
8936
  if (!sourceHandlesEqual(shape.handle, handle)) return true;
8220
8937
  found2 = shape;
8221
- if (shape.kind !== "shape") {
8222
- throw new Error("deleteShape: only top-level sp shapes can be deleted");
8938
+ if (shape.kind !== "shape" && shape.kind !== "connector") {
8939
+ throw new Error("deleteShape: only top-level sp or cxnSp shapes can be deleted");
8223
8940
  }
8224
8941
  if (hasAlternateContentSidecar(shape)) {
8225
8942
  throw new Error("deleteShape: shapes inside AlternateContent are not supported");
@@ -8259,22 +8976,310 @@ function deleteShape(source, handle) {
8259
8976
  ]
8260
8977
  };
8261
8978
  }
8262
- function shapeTransformPositionAndSizeEqual(current, next) {
8263
- return current?.offsetX === next.offsetX && current.offsetY === next.offsetY && current.width === next.width && current.height === next.height;
8979
+ function shapeTransformPositionAndSizeEqual(current, next) {
8980
+ return current?.offsetX === next.offsetX && current.offsetY === next.offsetY && current.width === next.width && current.height === next.height;
8981
+ }
8982
+ function patchSourceOutline(current, patch) {
8983
+ return {
8984
+ ...current ?? {},
8985
+ ...patch.width !== void 0 ? { width: patch.width } : {},
8986
+ ...patch.fill !== void 0 ? { fill: toSourceFill(patch.fill) } : {}
8987
+ };
8988
+ }
8989
+ function appendShapeFillEdit(edits, handle, fill) {
8990
+ const retainedEdits = edits.filter(
8991
+ (edit) => edit.kind !== "updateShapeFill" || !sourceHandlesEqual(edit.handle, handle)
8992
+ );
8993
+ return [...retainedEdits, { kind: "updateShapeFill", handle, fill }];
8994
+ }
8995
+ function appendShapeOutlineEdit(edits, handle, outline) {
8996
+ let outlineEdit = {
8997
+ kind: "updateShapeOutline",
8998
+ handle,
8999
+ outline
9000
+ };
9001
+ const retainedEdits = [];
9002
+ for (const edit of edits) {
9003
+ if (edit.kind !== "updateShapeOutline" || !sourceHandlesEqual(edit.handle, handle)) {
9004
+ retainedEdits.push(edit);
9005
+ continue;
9006
+ }
9007
+ outlineEdit = {
9008
+ ...outlineEdit,
9009
+ outline: mergeEditableShapeOutline(edit.outline, outlineEdit.outline)
9010
+ };
9011
+ }
9012
+ return [...retainedEdits, outlineEdit];
9013
+ }
9014
+ function mergeEditableShapeOutline(base, patch) {
9015
+ return {
9016
+ ...base.width !== void 0 ? { width: base.width } : {},
9017
+ ...base.fill !== void 0 ? { fill: base.fill } : {},
9018
+ ...patch.width !== void 0 ? { width: patch.width } : {},
9019
+ ...patch.fill !== void 0 ? { fill: patch.fill } : {}
9020
+ };
9021
+ }
9022
+ function toSourceFill(fill) {
9023
+ if (fill.kind === "none") return { kind: "none" };
9024
+ return {
9025
+ kind: "solid",
9026
+ color: { kind: "srgb", hex: fill.color.hex }
9027
+ };
9028
+ }
9029
+ function assertEditableShapeFill(fill, operationName) {
9030
+ if (fill.kind === "none") return;
9031
+ if (fill.kind !== "solid") {
9032
+ throw new Error(`${operationName}: only solid and none fills are supported`);
9033
+ }
9034
+ if (fill.color.kind !== "srgb") {
9035
+ throw new Error(`${operationName}: only srgb solid fill colors are supported`);
9036
+ }
9037
+ if (!/^[0-9A-Fa-f]{6}$/.test(fill.color.hex)) {
9038
+ throw new Error(`${operationName}: srgb fill color must be a 6-digit hex value`);
9039
+ }
9040
+ }
9041
+ function assertEditableShapeOutline(outline, operationName) {
9042
+ if (outline.width === void 0 && outline.fill === void 0) {
9043
+ throw new Error(`${operationName}: outline must set width or fill`);
9044
+ }
9045
+ if (outline.width !== void 0) {
9046
+ assertPositiveFiniteEmu(outline.width, operationName, "width");
9047
+ }
9048
+ if (outline.fill !== void 0) assertEditableShapeFill(outline.fill, operationName);
9049
+ }
9050
+ function sourceFillEqual(left, right) {
9051
+ return stableValueEqual(left ?? {}, right ?? {});
9052
+ }
9053
+ function sourceOutlineEqual(left, right) {
9054
+ return stableValueEqual(left ?? {}, right ?? {});
9055
+ }
9056
+ function stableValueEqual(left, right) {
9057
+ if (Object.is(left, right)) return true;
9058
+ if (Array.isArray(left) || Array.isArray(right)) {
9059
+ if (!Array.isArray(left) || !Array.isArray(right)) return false;
9060
+ if (left.length !== right.length) return false;
9061
+ return left.every((value, index) => stableValueEqual(value, right[index]));
9062
+ }
9063
+ if (isPlainRecord(left) || isPlainRecord(right)) {
9064
+ if (!isPlainRecord(left) || !isPlainRecord(right)) return false;
9065
+ const leftKeys = Object.keys(left).sort();
9066
+ const rightKeys = Object.keys(right).sort();
9067
+ if (!stableValueEqual(leftKeys, rightKeys)) return false;
9068
+ return leftKeys.every((key) => stableValueEqual(left[key], right[key]));
9069
+ }
9070
+ return false;
9071
+ }
9072
+ function isPlainRecord(value) {
9073
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9074
+ }
9075
+ function hasEditableTransform(shape) {
9076
+ return shape.kind !== "raw" && shape.transform !== void 0;
9077
+ }
9078
+ function assertTextBoxInput(input) {
9079
+ assertFiniteEmu(input.offsetX, "addTextBox", "offsetX");
9080
+ assertFiniteEmu(input.offsetY, "addTextBox", "offsetY");
9081
+ assertPositiveFiniteEmu(input.width, "addTextBox", "width");
9082
+ assertPositiveFiniteEmu(input.height, "addTextBox", "height");
9083
+ if (input.rotation !== void 0) {
9084
+ assertFiniteIntegerNumber(input.rotation, "addTextBox", "rotation");
9085
+ }
9086
+ if (input.text !== void 0 && input.paragraphs !== void 0) {
9087
+ throw new Error("addTextBox: specify either text or paragraphs, not both");
9088
+ }
9089
+ if (input.text === void 0 && input.paragraphs === void 0) {
9090
+ throw new Error("addTextBox: text or paragraphs must be provided");
9091
+ }
9092
+ if (input.text !== void 0 && typeof input.text !== "string") {
9093
+ throw new Error("addTextBox: text must be a string when provided");
9094
+ }
9095
+ if (input.paragraphs !== void 0) {
9096
+ assertTextBoxParagraphs(input.paragraphs);
9097
+ }
9098
+ if (input.body !== void 0) {
9099
+ assertTextBoxBody(input.body, "body");
9100
+ }
9101
+ if (input.name !== void 0 && input.name.trim() === "") {
9102
+ throw new Error("addTextBox: name must be a non-empty string when provided");
9103
+ }
9104
+ }
9105
+ function assertTextBoxParagraphs(paragraphs) {
9106
+ if (!isArrayValue(paragraphs) || paragraphs.length === 0) {
9107
+ throw new Error("addTextBox: paragraphs must contain at least one paragraph");
9108
+ }
9109
+ paragraphs.forEach((paragraph, paragraphIndex) => {
9110
+ if (!isPlainRecord(paragraph)) {
9111
+ throw new Error(`addTextBox: paragraphs[${paragraphIndex}] must be an object`);
9112
+ }
9113
+ const runs = paragraph.runs;
9114
+ if (!isArrayValue(runs) || runs.length === 0) {
9115
+ throw new Error(
9116
+ `addTextBox: paragraphs[${paragraphIndex}].runs must contain at least one run`
9117
+ );
9118
+ }
9119
+ assertTextBoxParagraphProperties(paragraph.properties, paragraphIndex);
9120
+ runs.forEach((run, runIndex) => assertTextBoxRun(run, paragraphIndex, runIndex));
9121
+ });
9122
+ }
9123
+ function assertTextBoxParagraphProperties(properties, paragraphIndex) {
9124
+ if (properties === void 0) return;
9125
+ if (!isPlainRecord(properties)) {
9126
+ throw new Error(`addTextBox: paragraphs[${paragraphIndex}].properties must be an object`);
9127
+ }
9128
+ const align = properties.align;
9129
+ const lineSpacing = properties.lineSpacing;
9130
+ if (align !== void 0 && align !== "left" && align !== "center" && align !== "right" && align !== "justify") {
9131
+ throw new Error(`addTextBox: paragraphs[${paragraphIndex}].properties.align is not supported`);
9132
+ }
9133
+ if (lineSpacing !== void 0) {
9134
+ assertPositiveFiniteIntegerNumber(
9135
+ lineSpacing,
9136
+ "addTextBox",
9137
+ `paragraphs[${paragraphIndex}].properties.lineSpacing`
9138
+ );
9139
+ }
9140
+ }
9141
+ function assertTextBoxRun(run, paragraphIndex, runIndex) {
9142
+ const path = `paragraphs[${paragraphIndex}].runs[${runIndex}]`;
9143
+ if (!isPlainRecord(run)) {
9144
+ throw new Error(`addTextBox: ${path} must be an object`);
9145
+ }
9146
+ if (typeof run.text !== "string") {
9147
+ throw new Error(`addTextBox: ${path}.text must be a string`);
9148
+ }
9149
+ assertTextBoxRunProperties(run.properties, path);
9150
+ }
9151
+ function assertTextBoxRunProperties(properties, path) {
9152
+ if (properties === void 0) return;
9153
+ if (!isPlainRecord(properties)) {
9154
+ throw new Error(`addTextBox: ${path}.properties must be an object`);
9155
+ }
9156
+ const fontFace = properties.fontFace;
9157
+ const fontSize = properties.fontSize;
9158
+ const color = properties.color;
9159
+ const gradientFill = properties.gradientFill;
9160
+ const underline = properties.underline;
9161
+ const baseline = properties.baseline;
9162
+ const highlight = properties.highlight;
9163
+ const glow = properties.glow;
9164
+ const outline = properties.outline;
9165
+ const charSpacing = properties.charSpacing;
9166
+ if (fontFace !== void 0 && (typeof fontFace !== "string" || fontFace.trim() === "")) {
9167
+ throw new Error(`addTextBox: ${path}.properties.fontFace must be a non-empty string`);
9168
+ }
9169
+ if (fontSize !== void 0) {
9170
+ assertPositiveFiniteNumber(fontSize, "addTextBox", `${path}.properties.fontSize`);
9171
+ }
9172
+ assertBooleanOrUndefined(properties.bold, "addTextBox", `${path}.properties.bold`);
9173
+ assertBooleanOrUndefined(properties.italic, "addTextBox", `${path}.properties.italic`);
9174
+ assertBooleanOrUndefined(properties.strike, "addTextBox", `${path}.properties.strike`);
9175
+ if (color !== void 0) assertTextBoxColor(color, `${path}.properties.color`);
9176
+ if (gradientFill !== void 0) {
9177
+ if (color !== void 0) {
9178
+ throw new Error(`addTextBox: ${path}.properties cannot set both color and gradientFill`);
9179
+ }
9180
+ assertTextBoxGradientFill(gradientFill, `${path}.properties.gradientFill`);
9181
+ }
9182
+ if (underline !== void 0) {
9183
+ assertTextBoxUnderline(underline, `${path}.properties.underline`);
9184
+ }
9185
+ if (baseline !== void 0) {
9186
+ assertTextBoxBaseline(baseline, `${path}.properties.baseline`);
9187
+ }
9188
+ if (highlight !== void 0) {
9189
+ assertTextBoxColor(highlight, `${path}.properties.highlight`);
9190
+ }
9191
+ if (glow !== void 0) assertTextBoxGlow(glow, `${path}.properties.glow`);
9192
+ if (outline !== void 0) {
9193
+ assertTextBoxOutline(outline, `${path}.properties.outline`);
9194
+ }
9195
+ if (charSpacing !== void 0) {
9196
+ assertTextPointNumber(charSpacing, "addTextBox", `${path}.properties.charSpacing`);
9197
+ }
9198
+ }
9199
+ function assertTextBoxColor(color, path) {
9200
+ if (!isPlainRecord(color)) {
9201
+ throw new Error(`addTextBox: ${path} must be an srgb 6-digit hex color`);
9202
+ }
9203
+ if (color.kind !== "srgb" || typeof color.hex !== "string" || !/^[0-9A-Fa-f]{6}$/.test(color.hex)) {
9204
+ throw new Error(`addTextBox: ${path} must be an srgb 6-digit hex color`);
9205
+ }
9206
+ }
9207
+ function assertTextBoxGradientFill(fill, path) {
9208
+ if (!isPlainRecord(fill)) {
9209
+ throw new Error(`addTextBox: ${path} must be a gradient fill object`);
9210
+ }
9211
+ const stops = fill.stops;
9212
+ if (!isArrayValue(stops) || stops.length < 2) {
9213
+ throw new Error(`addTextBox: ${path}.stops must contain at least two stops`);
9214
+ }
9215
+ if (fill.angle !== void 0)
9216
+ assertFiniteIntegerNumber(fill.angle, "addTextBox", `${path}.angle`);
9217
+ stops.forEach((stop, index) => {
9218
+ if (!isPlainRecord(stop)) {
9219
+ throw new Error(`addTextBox: ${path}.stops[${index}] must be an object`);
9220
+ }
9221
+ const position = stop.position;
9222
+ assertFiniteIntegerNumber(position, "addTextBox", `${path}.stops[${index}].position`);
9223
+ if (typeof position !== "number" || position < 0 || position > 1e5) {
9224
+ throw new Error(`addTextBox: ${path}.stops[${index}].position must be between 0 and 100000`);
9225
+ }
9226
+ assertTextBoxColor(stop.color, `${path}.stops[${index}].color`);
9227
+ });
9228
+ }
9229
+ function isArrayValue(value) {
9230
+ return Array.isArray(value);
8264
9231
  }
8265
- function hasEditableTransform(shape) {
8266
- return shape.kind !== "raw" && shape.transform !== void 0;
9232
+ function assertTextBoxUnderline(underline, path) {
9233
+ if (typeof underline === "boolean") return;
9234
+ if (!isPlainRecord(underline)) {
9235
+ throw new Error(`addTextBox: ${path} must be a boolean or underline options object`);
9236
+ }
9237
+ const style = underline.style ?? "sng";
9238
+ if (typeof style !== "string" || !UNDERLINE_STYLES.has(style)) {
9239
+ throw new Error(`addTextBox: ${path}.style is not supported`);
9240
+ }
9241
+ if (underline.color !== void 0) {
9242
+ assertTextBoxColor(underline.color, `${path}.color`);
9243
+ }
8267
9244
  }
8268
- function assertTextBoxInput(input) {
8269
- assertFiniteEmu(input.offsetX, "addTextBox", "offsetX");
8270
- assertFiniteEmu(input.offsetY, "addTextBox", "offsetY");
8271
- assertPositiveFiniteEmu(input.width, "addTextBox", "width");
8272
- assertPositiveFiniteEmu(input.height, "addTextBox", "height");
8273
- if (typeof input.text !== "string") {
8274
- throw new Error("addTextBox: text must be a string");
9245
+ function assertTextBoxBaseline(baseline, path) {
9246
+ if (baseline === "subscript" || baseline === "superscript") return;
9247
+ throw new Error(`addTextBox: ${path} must be subscript or superscript`);
9248
+ }
9249
+ function assertTextBoxGlow(glow, path) {
9250
+ if (!isPlainRecord(glow)) {
9251
+ throw new Error(`addTextBox: ${path} must be an object`);
8275
9252
  }
8276
- if (input.name !== void 0 && input.name.trim() === "") {
8277
- throw new Error("addTextBox: name must be a non-empty string when provided");
9253
+ assertPositiveFiniteEmu(glow.radius, "addTextBox", `${path}.radius`);
9254
+ assertTextBoxColor(glow.color, `${path}.color`);
9255
+ }
9256
+ function assertTextBoxOutline(outline, path) {
9257
+ if (!isPlainRecord(outline)) {
9258
+ throw new Error(`addTextBox: ${path} must be an object`);
9259
+ }
9260
+ if (outline.width === void 0 && outline.color === void 0) {
9261
+ throw new Error(`addTextBox: ${path} must set width or color`);
9262
+ }
9263
+ if (outline.width !== void 0) {
9264
+ assertPositiveFiniteEmu(outline.width, "addTextBox", `${path}.width`);
9265
+ }
9266
+ if (outline.color !== void 0) assertTextBoxColor(outline.color, `${path}.color`);
9267
+ }
9268
+ function assertTextBoxBody(body, path) {
9269
+ if (!isPlainRecord(body)) {
9270
+ throw new Error(`addTextBox: ${path} must be an object`);
9271
+ }
9272
+ if (body.anchor !== void 0 && body.anchor !== "top" && body.anchor !== "middle" && body.anchor !== "bottom") {
9273
+ throw new Error("addTextBox: body.anchor is not supported");
9274
+ }
9275
+ if (body.marginLeft !== void 0)
9276
+ assertFiniteEmu(body.marginLeft, "addTextBox", "body.marginLeft");
9277
+ if (body.marginRight !== void 0) {
9278
+ assertFiniteEmu(body.marginRight, "addTextBox", "body.marginRight");
9279
+ }
9280
+ if (body.marginTop !== void 0) assertFiniteEmu(body.marginTop, "addTextBox", "body.marginTop");
9281
+ if (body.marginBottom !== void 0) {
9282
+ assertFiniteEmu(body.marginBottom, "addTextBox", "body.marginBottom");
8278
9283
  }
8279
9284
  }
8280
9285
  function assertConnectorInput(input) {
@@ -8287,8 +9292,9 @@ function assertConnectorInput(input) {
8287
9292
  "addConnector: preset must be straightConnector1, bentConnector3, or curvedConnector3"
8288
9293
  );
8289
9294
  }
8290
- assertConnectionSiteIndex(input.start.connectionSiteIndex, "start");
8291
- assertConnectionSiteIndex(input.end.connectionSiteIndex, "end");
9295
+ if (input.start !== void 0)
9296
+ assertConnectionSiteIndex(input.start.connectionSiteIndex, "start");
9297
+ if (input.end !== void 0) assertConnectionSiteIndex(input.end.connectionSiteIndex, "end");
8292
9298
  if (input.name !== void 0 && input.name.trim() === "") {
8293
9299
  throw new Error("addConnector: name must be a non-empty string when provided");
8294
9300
  }
@@ -8304,23 +9310,49 @@ function assertConnectionSiteIndex(value, fieldName) {
8304
9310
  }
8305
9311
  function assertArrowEndpoint(endpoint, fieldName) {
8306
9312
  if (endpoint === void 0) return;
8307
- if (!ARROW_TYPES.has(endpoint.type)) {
9313
+ if (!ARROW_TYPES2.has(endpoint.type)) {
8308
9314
  throw new Error(`addConnector: outline.${fieldName}.type is not supported`);
8309
9315
  }
8310
- if (!ARROW_SIZES.has(endpoint.width)) {
9316
+ if (!ARROW_SIZES2.has(endpoint.width)) {
8311
9317
  throw new Error(`addConnector: outline.${fieldName}.width is not supported`);
8312
9318
  }
8313
- if (!ARROW_SIZES.has(endpoint.length)) {
9319
+ if (!ARROW_SIZES2.has(endpoint.length)) {
8314
9320
  throw new Error(`addConnector: outline.${fieldName}.length is not supported`);
8315
9321
  }
8316
9322
  }
9323
+ function assertBooleanOrUndefined(value, operationName, fieldName) {
9324
+ if (value !== void 0 && typeof value !== "boolean") {
9325
+ throw new Error(`${operationName}: ${fieldName} must be a boolean value`);
9326
+ }
9327
+ }
9328
+ function assertFiniteIntegerNumber(value, operationName, fieldName) {
9329
+ if (!Number.isInteger(value)) {
9330
+ throw new Error(`${operationName}: ${fieldName} must be a finite integer`);
9331
+ }
9332
+ }
9333
+ function assertPositiveFiniteNumber(value, operationName, fieldName) {
9334
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
9335
+ throw new Error(`${operationName}: ${fieldName} must be a finite positive number`);
9336
+ }
9337
+ }
9338
+ function assertPositiveFiniteIntegerNumber(value, operationName, fieldName) {
9339
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
9340
+ throw new Error(`${operationName}: ${fieldName} must be a finite positive integer`);
9341
+ }
9342
+ }
9343
+ function assertTextPointNumber(value, operationName, fieldName) {
9344
+ assertFiniteIntegerNumber(value, operationName, fieldName);
9345
+ if (typeof value !== "number" || value < -4e5 || value > 4e5) {
9346
+ throw new Error(`${operationName}: ${fieldName} must be between -400000 and 400000`);
9347
+ }
9348
+ }
8317
9349
  function assertFiniteEmu(value, operationName, fieldName) {
8318
- if (!Number.isFinite(value)) {
9350
+ if (typeof value !== "number" || !Number.isFinite(value)) {
8319
9351
  throw new Error(`${operationName}: ${fieldName} must be a finite EMU value`);
8320
9352
  }
8321
9353
  }
8322
9354
  function assertPositiveFiniteEmu(value, operationName, fieldName) {
8323
- if (!Number.isFinite(value) || value <= 0) {
9355
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
8324
9356
  throw new Error(`${operationName}: ${fieldName} must be a finite positive EMU value`);
8325
9357
  }
8326
9358
  }
@@ -8705,848 +9737,621 @@ function duplicateSlide(source, slideHandle) {
8705
9737
  ]
8706
9738
  };
8707
9739
  }
8708
- function deleteSlide(source, slideHandle) {
8709
- if (source.slides.length <= 1) {
8710
- throw new Error("deleteSlide: cannot delete the last slide");
8711
- }
9740
+ function moveSlide(source, slideHandle, input) {
9741
+ assertValidSlideIndex(input.toIndex, source.slides.length, "moveSlide");
8712
9742
  const slideIndex = source.slides.findIndex(
8713
9743
  (slide2) => sourceHandlesEqual(slide2.handle, slideHandle)
8714
9744
  );
8715
9745
  if (slideIndex === -1) {
8716
- throw new Error("deleteSlide: slide handle was not found in PptxSourceModel source");
9746
+ throw new Error("moveSlide: slide handle was not found in PptxSourceModel source");
8717
9747
  }
9748
+ if (slideIndex === input.toIndex) return source;
8718
9749
  const slide = source.slides[slideIndex];
8719
9750
  const presentationRels = requirePartRelationships(
8720
9751
  source,
8721
9752
  source.presentation.partPath,
8722
- "deleteSlide"
9753
+ "moveSlide"
8723
9754
  );
8724
9755
  const presentationRelationship = requireSlideRelationship(
8725
9756
  source,
8726
9757
  presentationRels,
8727
9758
  slide.partPath,
8728
- "deleteSlide"
8729
- );
8730
- const slideRelationships = source.packageGraph.relationships.find(
8731
- (relationships) => relationships.sourcePartPath === slide.partPath
8732
- );
8733
- const notesPartPaths = slideRelationships?.relationships.flatMap((relationship) => {
8734
- if (relationship.type !== NOTES_SLIDE_REL_TYPE) return [];
8735
- const target = resolveInternalRelationshipTarget(slide.partPath, relationship);
8736
- return target === void 0 ? [] : [target];
8737
- }) ?? [];
8738
- const removedPartPaths = [slide.partPath, ...notesPartPaths];
8739
- const removedPartPathSet = new Set(removedPartPaths);
8740
- const retainedEdits = (source.edits ?? []).filter(
8741
- (edit) => !editIsInvalidatedByDeletedParts(edit, removedPartPathSet)
8742
- );
8743
- const deletedInsertedSlide = (source.edits ?? []).some(
8744
- (edit) => editInsertedSlidePartPath(edit) === slide.partPath
8745
- );
8746
- const packageGraph = removePartRelationship(
8747
- removePackageParts(source.packageGraph, removedPartPaths),
8748
- source.presentation.partPath,
8749
- presentationRelationship.id
9759
+ "moveSlide"
8750
9760
  );
8751
9761
  return {
8752
9762
  ...source,
8753
9763
  presentation: {
8754
9764
  ...source.presentation,
8755
- slidePartPaths: source.presentation.slidePartPaths.filter(
8756
- (partPath) => partPath !== slide.partPath
8757
- )
9765
+ slidePartPaths: moveReadonly(source.presentation.slidePartPaths, slideIndex, input.toIndex)
8758
9766
  },
8759
- slides: source.slides.filter((candidate) => candidate.partPath !== slide.partPath),
8760
- packageGraph,
8761
- edits: deletedInsertedSlide ? retainedEdits : [
8762
- ...retainedEdits,
9767
+ slides: moveReadonly(source.slides, slideIndex, input.toIndex),
9768
+ edits: [
9769
+ ...source.edits ?? [],
8763
9770
  {
8764
- kind: "deleteSlide",
9771
+ kind: "moveSlide",
8765
9772
  slidePartPath: slide.partPath,
8766
- relationshipId: presentationRelationship.id
9773
+ relationshipId: presentationRelationship.id,
9774
+ toIndex: input.toIndex
8767
9775
  }
8768
9776
  ]
8769
9777
  };
8770
9778
  }
8771
- function createNotesSlideCopy(source, sourceSlide, slideRelationships, newSlidePartPath) {
8772
- const notesRelationship = slideRelationships?.relationships.find(
8773
- (relationship) => relationship.type === NOTES_SLIDE_REL_TYPE
8774
- );
8775
- if (notesRelationship === void 0) return void 0;
8776
- const notesPartPath = resolveInternalRelationshipTarget(sourceSlide.partPath, notesRelationship);
8777
- if (notesPartPath === void 0) return void 0;
8778
- const raw = requireRawBinaryPart(source, notesPartPath, "duplicateSlide");
8779
- const contentType = source.packageGraph.parts.find((part) => part.partPath === notesPartPath)?.contentType ?? NOTES_SLIDE_CONTENT_TYPE;
8780
- const newPartPath = nextNumberedPartPath(
8781
- source.packageGraph,
8782
- source.edits?.flatMap((edit) => editReservedPartPaths(edit)) ?? [],
8783
- "ppt/notesSlides/notesSlide",
8784
- ".xml"
8785
- );
8786
- const notesRelationships = source.packageGraph.relationships.find(
8787
- (relationships) => relationships.sourcePartPath === notesPartPath
8788
- );
8789
- return {
8790
- slideRelationshipId: notesRelationship.id,
8791
- newPartPath,
8792
- contentType,
8793
- raw,
8794
- ...notesRelationships === void 0 ? {} : {
8795
- relationships: {
8796
- sourcePartPath: newPartPath,
8797
- relationships: notesRelationships.relationships.map(
8798
- (relationship) => relationship.type === SLIDE_REL_TYPE && relationship.targetMode !== "External" ? { ...relationship, target: relativeTarget(newPartPath, newSlidePartPath) } : relationship
8799
- )
8800
- }
8801
- }
8802
- };
8803
- }
8804
- function withPartPath(slide, partPath) {
8805
- return {
8806
- ...slide,
8807
- partPath,
8808
- handle: { partPath },
8809
- shapes: slide.shapes.map((shape) => withShapePartPath(shape, partPath))
8810
- };
8811
- }
8812
- function withShapePartPath(shape, partPath) {
8813
- if (shape.kind === "raw") return shape;
8814
- const handle = shape.handle === void 0 ? void 0 : { ...shape.handle, partPath };
8815
- if (shape.kind === "group") {
8816
- return {
8817
- ...shape,
8818
- ...handle !== void 0 ? { handle } : {},
8819
- children: shape.children.map((child) => withShapePartPath(child, partPath))
8820
- };
8821
- }
8822
- if (shape.kind !== "shape" || shape.textBody === void 0) {
8823
- return { ...shape, ...handle !== void 0 ? { handle } : {} };
8824
- }
8825
- return {
8826
- ...shape,
8827
- ...handle !== void 0 ? { handle } : {},
8828
- textBody: {
8829
- ...shape.textBody,
8830
- paragraphs: shape.textBody.paragraphs.map((paragraph) => ({
8831
- ...paragraph,
8832
- ...paragraph.handle !== void 0 ? { handle: { ...paragraph.handle, partPath } } : {},
8833
- runs: paragraph.runs.map((run) => ({
8834
- ...run,
8835
- ...run.handle !== void 0 ? { handle: { ...run.handle, partPath } } : {}
8836
- }))
8837
- }))
8838
- }
8839
- };
8840
- }
8841
- var EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS = {
8842
- bold: (value) => requireBooleanOrUndefined(value, "bold"),
8843
- italic: (value) => requireBooleanOrUndefined(value, "italic"),
8844
- underline: (value) => requireBooleanOrUndefined(value, "underline"),
8845
- fontSize: (value) => {
8846
- if (value !== void 0 && (!Number.isFinite(value) || value <= 0)) {
8847
- throw new Error("updateTextRunProperties: fontSize must be a finite positive pt value");
8848
- }
8849
- },
8850
- color: (value) => {
8851
- if (value === void 0) return;
8852
- if (value.kind !== "srgb") {
8853
- throw new Error("updateTextRunProperties: only srgb text run color is supported");
8854
- }
8855
- if (!/^[0-9A-Fa-f]{6}$/.test(value.hex)) {
8856
- throw new Error("updateTextRunProperties: srgb text run color must be a 6-digit hex value");
8857
- }
8858
- },
8859
- typeface: (value) => {
8860
- if (value !== void 0 && value.trim() === "") {
8861
- throw new Error("updateTextRunProperties: typeface must be a non-empty string");
8862
- }
8863
- }
8864
- };
8865
- var EDITABLE_TEXT_RUN_PROPERTIES = Object.keys(EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS).filter(
8866
- (property) => property in EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS
8867
- );
8868
- var EDITABLE_TEXT_RUN_PROPERTY_SET = new Set(EDITABLE_TEXT_RUN_PROPERTIES);
8869
- function replaceTextRunPlainText(source, handle, text) {
8870
- const result = mapMatchingTextRun(
8871
- source,
8872
- handle,
8873
- (run) => run.text === text ? run : { ...run, text }
8874
- );
8875
- if (!result.matched) {
8876
- throw new Error(
8877
- "replaceTextRunPlainText: text run handle was not found in PptxSourceModel source"
8878
- );
8879
- }
8880
- if (!result.changed) return source;
8881
- return {
8882
- ...source,
8883
- slides: result.slides,
8884
- edits: [...source.edits ?? [], { kind: "replaceTextRunPlainText", handle, text }]
8885
- };
8886
- }
8887
- function setTextRunProperties(source, handle, properties) {
8888
- return updateTextRunProperties(source, handle, {
8889
- set: properties,
8890
- clear: []
8891
- });
8892
- }
8893
- function clearTextRunProperties(source, handle, properties) {
8894
- return updateTextRunProperties(source, handle, {
8895
- set: {},
8896
- clear: properties
8897
- });
8898
- }
8899
- function replaceParagraphPlainText(source, handle, text) {
8900
- const result = mapMatchingParagraph(source, handle, (paragraph) => {
8901
- const replacementHandle = createReplacementRunHandle(paragraph);
8902
- return {
8903
- ...paragraph,
8904
- runs: [
8905
- {
8906
- kind: "textRun",
8907
- text,
8908
- ...paragraph.runs[0]?.properties !== void 0 ? { properties: paragraph.runs[0].properties } : {},
8909
- ...replacementHandle !== void 0 ? { handle: replacementHandle } : {}
8910
- }
8911
- ]
8912
- };
8913
- });
8914
- if (!result.matched) {
8915
- throw new Error(
8916
- "replaceParagraphPlainText: paragraph handle was not found in PptxSourceModel source"
8917
- );
8918
- }
8919
- return {
8920
- ...source,
8921
- slides: result.slides,
8922
- edits: [...source.edits ?? [], { kind: "replaceParagraphPlainText", handle, text }]
8923
- };
8924
- }
8925
- function updateTextRunProperties(source, handle, patch) {
8926
- assertEditableTextRunProperties(patch.set);
8927
- assertEditableTextRunPropertyNames(patch.clear);
8928
- const set = definedEditableTextRunProperties(patch.set);
8929
- if (Object.values(set).every((value) => value === void 0) && patch.clear.length === 0) {
8930
- throw new Error("updateTextRunProperties: patch must set or clear at least one property");
8931
- }
8932
- const result = mapMatchingTextRun(source, handle, (run) => {
8933
- const properties = patchTextRunProperties(run.properties, { set, clear: patch.clear });
8934
- if (textRunPropertiesEqual(run.properties, properties)) return run;
8935
- return {
8936
- kind: run.kind,
8937
- text: run.text,
8938
- ...run.handle !== void 0 ? { handle: run.handle } : {},
8939
- ...run.rawSidecars !== void 0 ? { rawSidecars: run.rawSidecars } : {},
8940
- ...properties !== void 0 ? { properties } : {}
8941
- };
8942
- });
8943
- if (!result.matched) {
8944
- throw new Error(
8945
- "updateTextRunProperties: text run handle was not found in PptxSourceModel source"
8946
- );
9779
+ function deleteSlide(source, slideHandle) {
9780
+ if (source.slides.length <= 1) {
9781
+ throw new Error("deleteSlide: cannot delete the last slide");
8947
9782
  }
8948
- if (!result.changed) return source;
9783
+ const slideIndex = source.slides.findIndex(
9784
+ (slide2) => sourceHandlesEqual(slide2.handle, slideHandle)
9785
+ );
9786
+ if (slideIndex === -1) {
9787
+ throw new Error("deleteSlide: slide handle was not found in PptxSourceModel source");
9788
+ }
9789
+ const slide = source.slides[slideIndex];
9790
+ const presentationRels = requirePartRelationships(
9791
+ source,
9792
+ source.presentation.partPath,
9793
+ "deleteSlide"
9794
+ );
9795
+ const presentationRelationship = requireSlideRelationship(
9796
+ source,
9797
+ presentationRels,
9798
+ slide.partPath,
9799
+ "deleteSlide"
9800
+ );
9801
+ const slideRelationships = source.packageGraph.relationships.find(
9802
+ (relationships) => relationships.sourcePartPath === slide.partPath
9803
+ );
9804
+ const notesPartPaths = slideRelationships?.relationships.flatMap((relationship) => {
9805
+ if (relationship.type !== NOTES_SLIDE_REL_TYPE) return [];
9806
+ const target = resolveInternalRelationshipTarget(slide.partPath, relationship);
9807
+ return target === void 0 ? [] : [target];
9808
+ }) ?? [];
9809
+ const removedPartPaths = [slide.partPath, ...notesPartPaths];
9810
+ const removedPartPathSet = new Set(removedPartPaths);
9811
+ const retainedEdits = (source.edits ?? []).filter(
9812
+ (edit) => !editIsInvalidatedByDeletedParts(edit, removedPartPathSet)
9813
+ );
9814
+ const deletedInsertedSlide = (source.edits ?? []).some(
9815
+ (edit) => editInsertedSlidePartPath(edit) === slide.partPath
9816
+ );
9817
+ const packageGraph = removePartRelationship(
9818
+ removePackageParts(source.packageGraph, removedPartPaths),
9819
+ source.presentation.partPath,
9820
+ presentationRelationship.id
9821
+ );
8949
9822
  return {
8950
9823
  ...source,
8951
- slides: result.slides,
8952
- edits: [
8953
- ...source.edits ?? [],
9824
+ presentation: {
9825
+ ...source.presentation,
9826
+ slidePartPaths: source.presentation.slidePartPaths.filter(
9827
+ (partPath) => partPath !== slide.partPath
9828
+ )
9829
+ },
9830
+ slides: source.slides.filter((candidate) => candidate.partPath !== slide.partPath),
9831
+ packageGraph,
9832
+ edits: deletedInsertedSlide ? retainedEdits : [
9833
+ ...retainedEdits,
8954
9834
  {
8955
- kind: "updateTextRunProperties",
8956
- handle,
8957
- ...Object.keys(set).length > 0 ? { set } : {},
8958
- ...patch.clear.length > 0 ? { clear: patch.clear } : {}
9835
+ kind: "deleteSlide",
9836
+ slidePartPath: slide.partPath,
9837
+ relationshipId: presentationRelationship.id
8959
9838
  }
8960
9839
  ]
8961
9840
  };
8962
9841
  }
8963
- function mapMatchingTextRun(source, handle, mapRun) {
8964
- return mapTextBodyParagraphs(source, (paragraph) => {
8965
- let matched = false;
8966
- let changed = false;
8967
- const runs = paragraph.runs.map((run) => {
8968
- if (!sourceHandlesEqual(run.handle, handle)) return run;
8969
- matched = true;
8970
- const mapped = mapRun(run);
8971
- if (mapped === run) return run;
8972
- changed = true;
8973
- return mapped;
8974
- });
8975
- return {
8976
- paragraph: changed ? { ...paragraph, runs } : paragraph,
8977
- matched
8978
- };
8979
- });
8980
- }
8981
- function mapMatchingParagraph(source, handle, mapParagraph) {
8982
- return mapTextBodyParagraphs(source, (paragraph) => {
8983
- if (!sourceHandlesEqual(paragraph.handle, handle)) {
8984
- return { paragraph, matched: false };
9842
+ function createNotesSlideCopy(source, sourceSlide, slideRelationships, newSlidePartPath) {
9843
+ const notesRelationship = slideRelationships?.relationships.find(
9844
+ (relationship) => relationship.type === NOTES_SLIDE_REL_TYPE
9845
+ );
9846
+ if (notesRelationship === void 0) return void 0;
9847
+ const notesPartPath = resolveInternalRelationshipTarget(sourceSlide.partPath, notesRelationship);
9848
+ if (notesPartPath === void 0) return void 0;
9849
+ const raw = requireRawBinaryPart(source, notesPartPath, "duplicateSlide");
9850
+ const contentType = source.packageGraph.parts.find((part) => part.partPath === notesPartPath)?.contentType ?? NOTES_SLIDE_CONTENT_TYPE;
9851
+ const newPartPath = nextNumberedPartPath(
9852
+ source.packageGraph,
9853
+ source.edits?.flatMap((edit) => editReservedPartPaths(edit)) ?? [],
9854
+ "ppt/notesSlides/notesSlide",
9855
+ ".xml"
9856
+ );
9857
+ const notesRelationships = source.packageGraph.relationships.find(
9858
+ (relationships) => relationships.sourcePartPath === notesPartPath
9859
+ );
9860
+ return {
9861
+ slideRelationshipId: notesRelationship.id,
9862
+ newPartPath,
9863
+ contentType,
9864
+ raw,
9865
+ ...notesRelationships === void 0 ? {} : {
9866
+ relationships: {
9867
+ sourcePartPath: newPartPath,
9868
+ relationships: notesRelationships.relationships.map(
9869
+ (relationship) => relationship.type === SLIDE_REL_TYPE && relationship.targetMode !== "External" ? { ...relationship, target: relativeTarget(newPartPath, newSlidePartPath) } : relationship
9870
+ )
9871
+ }
8985
9872
  }
8986
- return { paragraph: mapParagraph(paragraph), matched: true };
8987
- });
9873
+ };
8988
9874
  }
8989
- function mapTextBodyParagraphs(source, mapParagraph) {
8990
- let matched = false;
8991
- let changed = false;
8992
- const slides = source.slides.map((slide) => {
8993
- let slideChanged = false;
8994
- const shapes = slide.shapes.map((shape) => {
8995
- if (shape.kind !== "shape" || shape.textBody === void 0) return shape;
8996
- let shapeChanged = false;
8997
- const paragraphs = shape.textBody.paragraphs.map((paragraph) => {
8998
- const result = mapParagraph(paragraph);
8999
- if (result.matched) matched = true;
9000
- if (result.paragraph === paragraph) return paragraph;
9001
- changed = true;
9002
- shapeChanged = true;
9003
- slideChanged = true;
9004
- return result.paragraph;
9005
- });
9006
- if (!shapeChanged) return shape;
9007
- return {
9008
- ...shape,
9009
- textBody: {
9010
- ...shape.textBody,
9011
- paragraphs
9012
- }
9013
- };
9014
- });
9015
- return slideChanged ? { ...slide, shapes } : slide;
9016
- });
9875
+ function withPartPath(slide, partPath) {
9017
9876
  return {
9018
- slides: changed ? slides : source.slides,
9019
- matched,
9020
- changed
9877
+ ...slide,
9878
+ partPath,
9879
+ handle: { partPath },
9880
+ shapes: slide.shapes.map((shape) => withShapePartPath(shape, partPath))
9021
9881
  };
9022
9882
  }
9023
- function patchTextRunProperties(current, patch) {
9024
- const next = { ...current ?? {} };
9025
- for (const property of patch.clear) {
9026
- delete next[property];
9027
- }
9028
- Object.assign(next, patch.set);
9029
- return Object.keys(next).length > 0 ? next : void 0;
9030
- }
9031
- function assertEditableTextRunProperties(properties) {
9032
- for (const property of Object.keys(properties)) {
9033
- assertEditableTextRunPropertyName(property);
9034
- }
9035
- for (const property of EDITABLE_TEXT_RUN_PROPERTIES) {
9036
- validateEditableTextRunProperty(property, properties[property]);
9037
- }
9038
- }
9039
- function requireBooleanOrUndefined(value, fieldName) {
9040
- if (value !== void 0 && typeof value !== "boolean") {
9041
- throw new Error(`updateTextRunProperties: ${fieldName} must be a boolean value`);
9042
- }
9043
- }
9044
- function assertEditableTextRunPropertyNames(properties) {
9045
- for (const property of properties) {
9046
- assertEditableTextRunPropertyName(property);
9047
- }
9048
- }
9049
- function definedEditableTextRunProperties(properties) {
9050
- const defined = {};
9051
- for (const property of EDITABLE_TEXT_RUN_PROPERTIES) {
9052
- copyDefinedEditableTextRunProperty(defined, properties, property);
9053
- }
9054
- return defined;
9055
- }
9056
- function assertEditableTextRunPropertyName(property) {
9057
- if (!isEditableTextRunProperty(property)) {
9058
- throw new Error(`updateTextRunProperties: unsupported text run property '${property}'`);
9059
- }
9060
- }
9061
- function isEditableTextRunProperty(property) {
9062
- return EDITABLE_TEXT_RUN_PROPERTY_SET.has(property);
9063
- }
9064
- function validateEditableTextRunProperty(property, value) {
9065
- EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS[property](value);
9066
- }
9067
- function copyDefinedEditableTextRunProperty(target, source, property) {
9068
- const value = source[property];
9069
- if (value !== void 0) {
9070
- target[property] = value;
9071
- }
9072
- }
9073
- function textRunPropertiesEqual(left, right) {
9074
- return stableValueEqual(left ?? {}, right ?? {});
9075
- }
9076
- function stableValueEqual(left, right) {
9077
- if (Object.is(left, right)) return true;
9078
- if (Array.isArray(left) || Array.isArray(right)) {
9079
- if (!Array.isArray(left) || !Array.isArray(right)) return false;
9080
- if (left.length !== right.length) return false;
9081
- return left.every((value, index) => stableValueEqual(value, right[index]));
9883
+ function withShapePartPath(shape, partPath) {
9884
+ if (shape.kind === "raw") return shape;
9885
+ const handle = shape.handle === void 0 ? void 0 : { ...shape.handle, partPath };
9886
+ if (shape.kind === "group") {
9887
+ return {
9888
+ ...shape,
9889
+ ...handle !== void 0 ? { handle } : {},
9890
+ children: shape.children.map((child) => withShapePartPath(child, partPath))
9891
+ };
9082
9892
  }
9083
- if (isPlainRecord(left) || isPlainRecord(right)) {
9084
- if (!isPlainRecord(left) || !isPlainRecord(right)) return false;
9085
- const leftKeys = Object.keys(left).sort();
9086
- const rightKeys = Object.keys(right).sort();
9087
- if (!stableValueEqual(leftKeys, rightKeys)) return false;
9088
- return leftKeys.every((key) => stableValueEqual(left[key], right[key]));
9893
+ if (shape.kind !== "shape" || shape.textBody === void 0) {
9894
+ return { ...shape, ...handle !== void 0 ? { handle } : {} };
9089
9895
  }
9090
- return false;
9091
- }
9092
- function isPlainRecord(value) {
9093
- return typeof value === "object" && value !== null && !Array.isArray(value);
9094
- }
9095
- function createReplacementRunHandle(paragraph) {
9096
- if (paragraph.runs[0]?.handle !== void 0) return paragraph.runs[0].handle;
9097
- if (paragraph.handle?.nodeId === void 0) return void 0;
9098
9896
  return {
9099
- ...paragraph.handle,
9100
- nodeId: asSourceNodeId(`${paragraph.handle.nodeId}:r:0`),
9101
- orderingSlot: 0
9102
- };
9103
- }
9104
- function asEmu2(value) {
9105
- return unsafeBrandAssertion2(value);
9106
- }
9107
- function asPt(value) {
9108
- return unsafeBrandAssertion2(value);
9109
- }
9110
- function asHundredthPt2(value) {
9111
- return unsafeBrandAssertion2(value);
9897
+ ...shape,
9898
+ ...handle !== void 0 ? { handle } : {},
9899
+ textBody: {
9900
+ ...shape.textBody,
9901
+ paragraphs: shape.textBody.paragraphs.map((paragraph) => ({
9902
+ ...paragraph,
9903
+ ...paragraph.handle !== void 0 ? { handle: { ...paragraph.handle, partPath } } : {},
9904
+ runs: paragraph.runs.map((run) => ({
9905
+ ...run,
9906
+ ...run.handle !== void 0 ? { handle: { ...run.handle, partPath } } : {}
9907
+ }))
9908
+ }))
9909
+ }
9910
+ };
9112
9911
  }
9113
- function asOoxmlPercent(value) {
9114
- return unsafeBrandAssertion2(value);
9912
+ function assertValidSlideIndex(index, slideCount, operationName) {
9913
+ if (!Number.isInteger(index) || index < 0 || index >= slideCount) {
9914
+ throw new Error(`${operationName}: toIndex must be an integer slide index in range`);
9915
+ }
9115
9916
  }
9116
- function asOoxmlAngle(value) {
9117
- return unsafeBrandAssertion2(value);
9917
+ function moveReadonly(items, fromIndex, toIndex) {
9918
+ const moved = [...items];
9919
+ const [item] = moved.splice(fromIndex, 1);
9920
+ if (item === void 0) return items;
9921
+ moved.splice(toIndex, 0, item);
9922
+ return moved;
9118
9923
  }
9119
- var RECTANGLE_ALIGNMENT_TOKENS = /* @__PURE__ */ new Set([
9120
- "tl",
9121
- "t",
9122
- "tr",
9123
- "l",
9124
- "ctr",
9125
- "r",
9126
- "bl",
9127
- "b",
9128
- "br"
9129
- ]);
9130
- var COLOR_TRANSFORM_KINDS = /* @__PURE__ */ new Set([
9131
- "lumMod",
9132
- "lumOff",
9133
- "tint",
9134
- "shade",
9135
- "alpha"
9136
- ]);
9137
- var RAW_FILL_LOCAL_NAMES = ["grpFill"];
9138
- var PRESET_COLOR_HEX = {
9139
- black: "000000",
9140
- white: "FFFFFF",
9141
- red: "FF0000",
9142
- green: "008000",
9143
- blue: "0000FF",
9144
- yellow: "FFFF00",
9145
- cyan: "00FFFF",
9146
- magenta: "FF00FF"
9147
- };
9148
- function parseColorElement(parent) {
9149
- if (!parent) return void 0;
9150
- const srgb = getChild(parent, "srgbClr");
9151
- if (srgb) {
9152
- const hex = getAttr(srgb, "val");
9153
- if (hex !== void 0) {
9154
- return withTransforms({ kind: "srgb", hex: hex.toUpperCase() }, srgb);
9924
+ var EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS = {
9925
+ bold: (value) => requireBooleanOrUndefined(value, "bold"),
9926
+ italic: (value) => requireBooleanOrUndefined(value, "italic"),
9927
+ underline: (value) => requireBooleanOrUndefined(value, "underline"),
9928
+ fontSize: (value) => {
9929
+ if (value !== void 0 && (!Number.isFinite(value) || value <= 0)) {
9930
+ throw new Error("updateTextRunProperties: fontSize must be a finite positive pt value");
9155
9931
  }
9156
- }
9157
- const scheme = getChild(parent, "schemeClr");
9158
- if (scheme) {
9159
- const name = getAttr(scheme, "val");
9160
- if (name !== void 0) {
9161
- return withTransforms({ kind: "scheme", scheme: name }, scheme);
9932
+ },
9933
+ color: (value) => {
9934
+ if (value === void 0) return;
9935
+ if (value.kind !== "srgb") {
9936
+ throw new Error("updateTextRunProperties: only srgb text run color is supported");
9162
9937
  }
9163
- }
9164
- const sys = getChild(parent, "sysClr");
9165
- if (sys) {
9166
- const val = getAttr(sys, "val");
9167
- if (val !== void 0) {
9168
- const lastClr = getAttr(sys, "lastClr");
9169
- return withTransforms(
9170
- {
9171
- kind: "system",
9172
- value: val,
9173
- ...lastClr !== void 0 ? { lastColor: lastClr.toUpperCase() } : {}
9174
- },
9175
- sys
9176
- );
9938
+ if (!/^[0-9A-Fa-f]{6}$/.test(value.hex)) {
9939
+ throw new Error("updateTextRunProperties: srgb text run color must be a 6-digit hex value");
9940
+ }
9941
+ },
9942
+ typeface: (value) => {
9943
+ if (value !== void 0 && value.trim() === "") {
9944
+ throw new Error("updateTextRunProperties: typeface must be a non-empty string");
9177
9945
  }
9178
9946
  }
9179
- return void 0;
9180
- }
9181
- function parseEffectList(effectList) {
9182
- if (effectList === void 0) return void 0;
9183
- const outerShadow = parseOuterShadow(getChild(effectList, "outerShdw"));
9184
- const innerShadow = parseInnerShadow(getChild(effectList, "innerShdw"));
9185
- const glow = parseGlow(getChild(effectList, "glow"));
9186
- const softEdge = parseSoftEdge(getChild(effectList, "softEdge"));
9187
- const parsed = {
9188
- ...outerShadow !== void 0 ? { outerShadow } : {},
9189
- ...innerShadow !== void 0 ? { innerShadow } : {},
9190
- ...glow !== void 0 ? { glow } : {},
9191
- ...softEdge !== void 0 ? { softEdge } : {}
9947
+ };
9948
+ var EDITABLE_TEXT_RUN_PROPERTIES = Object.keys(EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS).filter(
9949
+ (property) => property in EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS
9950
+ );
9951
+ var EDITABLE_TEXT_RUN_PROPERTY_SET = new Set(EDITABLE_TEXT_RUN_PROPERTIES);
9952
+ var EDITABLE_PARAGRAPH_PROPERTIES = [
9953
+ "align",
9954
+ "level",
9955
+ "bullet"
9956
+ ];
9957
+ var EDITABLE_PARAGRAPH_PROPERTY_SET = new Set(EDITABLE_PARAGRAPH_PROPERTIES);
9958
+ var TEXT_ALIGN_VALUES = /* @__PURE__ */ new Set([
9959
+ "left",
9960
+ "center",
9961
+ "right",
9962
+ "justify"
9963
+ ]);
9964
+ var AUTO_NUM_SCHEMES = /* @__PURE__ */ new Set([
9965
+ "arabicPeriod",
9966
+ "arabicParenR",
9967
+ "romanUcPeriod",
9968
+ "romanLcPeriod",
9969
+ "alphaUcPeriod",
9970
+ "alphaLcPeriod",
9971
+ "alphaLcParenR",
9972
+ "alphaUcParenR",
9973
+ "arabicPlain"
9974
+ ]);
9975
+ function replaceTextRunPlainText(source, handle, text) {
9976
+ const result = mapMatchingTextRun(
9977
+ source,
9978
+ handle,
9979
+ (run) => run.text === text ? run : { ...run, text }
9980
+ );
9981
+ if (!result.matched) {
9982
+ throw new Error(
9983
+ "replaceTextRunPlainText: text run handle was not found in PptxSourceModel source"
9984
+ );
9985
+ }
9986
+ if (!result.changed) return source;
9987
+ return {
9988
+ ...source,
9989
+ slides: result.slides,
9990
+ edits: [...source.edits ?? [], { kind: "replaceTextRunPlainText", handle, text }]
9192
9991
  };
9193
- return Object.keys(parsed).length > 0 ? parsed : void 0;
9194
9992
  }
9195
- function parseBlipEffects(blip) {
9196
- if (blip === void 0) return void 0;
9197
- const grayscale = getChild(blip, "grayscl") !== void 0;
9198
- const biLevel = parseBiLevel(getChild(blip, "biLevel"));
9199
- const blur = parseBlurEffect(getChild(blip, "blur"));
9200
- const lum = parseLumEffect(getChild(blip, "lum"));
9201
- const duotone = parseDuotoneEffect(getChild(blip, "duotone"));
9202
- const clrChange = parseColorChangeEffect(getChild(blip, "clrChange"));
9203
- const parsed = {
9204
- grayscale,
9205
- ...biLevel !== void 0 ? { biLevel } : {},
9206
- ...blur !== void 0 ? { blur } : {},
9207
- ...lum !== void 0 ? { lum } : {},
9208
- ...duotone !== void 0 ? { duotone } : {},
9209
- ...clrChange !== void 0 ? { clrChange } : {}
9210
- };
9211
- return grayscale || biLevel !== void 0 || blur !== void 0 || lum !== void 0 || duotone !== void 0 || clrChange !== void 0 ? parsed : void 0;
9993
+ function setTextRunProperties(source, handle, properties) {
9994
+ return updateTextRunProperties(source, handle, {
9995
+ set: properties,
9996
+ clear: []
9997
+ });
9212
9998
  }
9213
- function parseOuterShadow(node) {
9214
- if (node === void 0) return void 0;
9215
- const color = parseColorElement(node);
9216
- if (color === void 0) return void 0;
9217
- return {
9218
- blurRadius: asEmu2(numericAttr2(node, "blurRad") ?? 0),
9219
- distance: asEmu2(numericAttr2(node, "dist") ?? 0),
9220
- direction: asOoxmlAngle(numericAttr2(node, "dir") ?? 0),
9221
- color,
9222
- alignment: parseRectangleAlignment(getAttr(node, "algn"), "b"),
9223
- rotateWithShape: getAttr(node, "rotWithShape") !== "0"
9224
- };
9999
+ function clearTextRunProperties(source, handle, properties) {
10000
+ return updateTextRunProperties(source, handle, {
10001
+ set: {},
10002
+ clear: properties
10003
+ });
9225
10004
  }
9226
- function parseInnerShadow(node) {
9227
- if (node === void 0) return void 0;
9228
- const color = parseColorElement(node);
9229
- if (color === void 0) return void 0;
9230
- return {
9231
- blurRadius: asEmu2(numericAttr2(node, "blurRad") ?? 0),
9232
- distance: asEmu2(numericAttr2(node, "dist") ?? 0),
9233
- direction: asOoxmlAngle(numericAttr2(node, "dir") ?? 0),
9234
- color
9235
- };
10005
+ function setParagraphProperties(source, handle, properties) {
10006
+ return updateParagraphProperties(source, handle, {
10007
+ set: properties,
10008
+ clear: []
10009
+ });
9236
10010
  }
9237
- function parseGlow(node) {
9238
- if (node === void 0) return void 0;
9239
- const color = parseColorElement(node);
9240
- if (color === void 0) return void 0;
10011
+ function clearParagraphProperties(source, handle, properties) {
10012
+ return updateParagraphProperties(source, handle, {
10013
+ set: {},
10014
+ clear: properties
10015
+ });
10016
+ }
10017
+ function replaceParagraphPlainText(source, handle, text) {
10018
+ const result = mapMatchingParagraph(source, handle, (paragraph) => {
10019
+ const replacementHandle = createReplacementRunHandle(paragraph);
10020
+ return {
10021
+ ...paragraph,
10022
+ runs: [
10023
+ {
10024
+ kind: "textRun",
10025
+ text,
10026
+ ...paragraph.runs[0]?.properties !== void 0 ? { properties: paragraph.runs[0].properties } : {},
10027
+ ...replacementHandle !== void 0 ? { handle: replacementHandle } : {}
10028
+ }
10029
+ ]
10030
+ };
10031
+ });
10032
+ if (!result.matched) {
10033
+ throw new Error(
10034
+ "replaceParagraphPlainText: paragraph handle was not found in PptxSourceModel source"
10035
+ );
10036
+ }
9241
10037
  return {
9242
- radius: asEmu2(numericAttr2(node, "rad") ?? 0),
9243
- color
10038
+ ...source,
10039
+ slides: result.slides,
10040
+ edits: [...source.edits ?? [], { kind: "replaceParagraphPlainText", handle, text }]
9244
10041
  };
9245
10042
  }
9246
- function parseSoftEdge(node) {
9247
- if (node === void 0) return void 0;
10043
+ function updateTextRunProperties(source, handle, patch) {
10044
+ assertEditableTextRunProperties(patch.set);
10045
+ assertEditableTextRunPropertyNames(patch.clear);
10046
+ const set = definedEditableTextRunProperties(patch.set);
10047
+ if (Object.values(set).every((value) => value === void 0) && patch.clear.length === 0) {
10048
+ throw new Error("updateTextRunProperties: patch must set or clear at least one property");
10049
+ }
10050
+ const result = mapMatchingTextRun(source, handle, (run) => {
10051
+ const properties = patchTextRunProperties(run.properties, { set, clear: patch.clear });
10052
+ if (textRunPropertiesEqual(run.properties, properties)) return run;
10053
+ return {
10054
+ kind: run.kind,
10055
+ text: run.text,
10056
+ ...run.handle !== void 0 ? { handle: run.handle } : {},
10057
+ ...run.rawSidecars !== void 0 ? { rawSidecars: run.rawSidecars } : {},
10058
+ ...properties !== void 0 ? { properties } : {}
10059
+ };
10060
+ });
10061
+ if (!result.matched) {
10062
+ throw new Error(
10063
+ "updateTextRunProperties: text run handle was not found in PptxSourceModel source"
10064
+ );
10065
+ }
10066
+ if (!result.changed) return source;
9248
10067
  return {
9249
- radius: asEmu2(numericAttr2(node, "rad") ?? 0)
10068
+ ...source,
10069
+ slides: result.slides,
10070
+ edits: [
10071
+ ...source.edits ?? [],
10072
+ {
10073
+ kind: "updateTextRunProperties",
10074
+ handle,
10075
+ ...Object.keys(set).length > 0 ? { set } : {},
10076
+ ...patch.clear.length > 0 ? { clear: patch.clear } : {}
10077
+ }
10078
+ ]
9250
10079
  };
9251
10080
  }
9252
- function parseBiLevel(node) {
9253
- if (node === void 0) return void 0;
10081
+ function updateParagraphProperties(source, handle, patch) {
10082
+ assertEditableParagraphProperties(patch.set);
10083
+ assertEditableParagraphPropertyNames(patch.clear);
10084
+ const set = definedEditableParagraphProperties(patch.set);
10085
+ if (Object.values(set).every((value) => value === void 0) && patch.clear.length === 0) {
10086
+ throw new Error("updateParagraphProperties: patch must set or clear at least one property");
10087
+ }
10088
+ const result = mapMatchingParagraph(source, handle, (paragraph) => {
10089
+ const properties = patchParagraphProperties(paragraph.properties, { set, clear: patch.clear });
10090
+ if (paragraphPropertiesEqual(paragraph.properties, properties)) return paragraph;
10091
+ return {
10092
+ runs: paragraph.runs,
10093
+ ...paragraph.handle !== void 0 ? { handle: paragraph.handle } : {},
10094
+ ...paragraph.rawSidecars !== void 0 ? { rawSidecars: paragraph.rawSidecars } : {},
10095
+ ...properties !== void 0 ? { properties } : {}
10096
+ };
10097
+ });
10098
+ if (!result.matched) {
10099
+ throw new Error(
10100
+ "updateParagraphProperties: paragraph handle was not found in PptxSourceModel source"
10101
+ );
10102
+ }
10103
+ if (!result.changed) return source;
9254
10104
  return {
9255
- threshold: (numericAttr2(node, "thresh") ?? 5e4) / 1e5
10105
+ ...source,
10106
+ slides: result.slides,
10107
+ edits: [
10108
+ ...source.edits ?? [],
10109
+ {
10110
+ kind: "updateParagraphProperties",
10111
+ handle,
10112
+ ...Object.keys(set).length > 0 ? { set } : {},
10113
+ ...patch.clear.length > 0 ? { clear: patch.clear } : {}
10114
+ }
10115
+ ]
9256
10116
  };
9257
10117
  }
9258
- function parseBlurEffect(node) {
9259
- if (node === void 0) return void 0;
9260
- return {
9261
- radius: asEmu2(numericAttr2(node, "rad") ?? 0),
9262
- grow: getAttr(node, "grow") !== "0"
9263
- };
10118
+ function mapMatchingTextRun(source, handle, mapRun) {
10119
+ return mapTextBodyParagraphs(source, (paragraph) => {
10120
+ let matched = false;
10121
+ let changed = false;
10122
+ const runs = paragraph.runs.map((run) => {
10123
+ if (!sourceHandlesEqual(run.handle, handle)) return run;
10124
+ matched = true;
10125
+ const mapped = mapRun(run);
10126
+ if (mapped === run) return run;
10127
+ changed = true;
10128
+ return mapped;
10129
+ });
10130
+ return {
10131
+ paragraph: changed ? { ...paragraph, runs } : paragraph,
10132
+ matched
10133
+ };
10134
+ });
9264
10135
  }
9265
- function parseLumEffect(node) {
9266
- if (node === void 0) return void 0;
10136
+ function mapMatchingParagraph(source, handle, mapParagraph) {
10137
+ return mapTextBodyParagraphs(source, (paragraph) => {
10138
+ if (!sourceHandlesEqual(paragraph.handle, handle)) {
10139
+ return { paragraph, matched: false };
10140
+ }
10141
+ return { paragraph: mapParagraph(paragraph), matched: true };
10142
+ });
10143
+ }
10144
+ function mapTextBodyParagraphs(source, mapParagraph) {
10145
+ let matched = false;
10146
+ let changed = false;
10147
+ const slides = source.slides.map((slide) => {
10148
+ let slideChanged = false;
10149
+ const shapes = slide.shapes.map((shape) => {
10150
+ if (shape.kind !== "shape" || shape.textBody === void 0) return shape;
10151
+ let shapeChanged = false;
10152
+ const paragraphs = shape.textBody.paragraphs.map((paragraph) => {
10153
+ const result = mapParagraph(paragraph);
10154
+ if (result.matched) matched = true;
10155
+ if (result.paragraph === paragraph) return paragraph;
10156
+ changed = true;
10157
+ shapeChanged = true;
10158
+ slideChanged = true;
10159
+ return result.paragraph;
10160
+ });
10161
+ if (!shapeChanged) return shape;
10162
+ return {
10163
+ ...shape,
10164
+ textBody: {
10165
+ ...shape.textBody,
10166
+ paragraphs
10167
+ }
10168
+ };
10169
+ });
10170
+ return slideChanged ? { ...slide, shapes } : slide;
10171
+ });
9267
10172
  return {
9268
- brightness: (numericAttr2(node, "bright") ?? 0) / 1e5,
9269
- contrast: (numericAttr2(node, "contrast") ?? 0) / 1e5
10173
+ slides: changed ? slides : source.slides,
10174
+ matched,
10175
+ changed
9270
10176
  };
9271
10177
  }
9272
- function parseDuotoneEffect(node) {
9273
- if (node === void 0) return void 0;
9274
- const colors = collectColorChildren(node);
9275
- if (colors.length < 2) return void 0;
9276
- return { color1: colors[0], color2: colors[1] };
9277
- }
9278
- function parseColorChangeEffect(node) {
9279
- if (node === void 0) return void 0;
9280
- const from = firstColorChild(getChild(node, "clrFrom"));
9281
- const to = firstColorChild(getChild(node, "clrTo"));
9282
- return from !== void 0 && to !== void 0 ? { from, to } : void 0;
9283
- }
9284
- function collectColorChildren(parent) {
9285
- const colors = [];
9286
- for (const [key, value] of Object.entries(parent)) {
9287
- if (key.startsWith("@_")) continue;
9288
- const nodes = Array.isArray(value) ? value : [value];
9289
- for (const node of nodes) {
9290
- if (!isXmlNode(node)) continue;
9291
- const color = parseColorChild(key, node);
9292
- if (color !== void 0) colors.push(color);
9293
- }
10178
+ function patchTextRunProperties(current, patch) {
10179
+ const next = { ...current ?? {} };
10180
+ for (const property of patch.clear) {
10181
+ delete next[property];
9294
10182
  }
9295
- return colors;
9296
- }
9297
- function firstColorChild(parent) {
9298
- return parent !== void 0 ? collectColorChildren(parent)[0] : void 0;
9299
- }
9300
- function parseColorChild(key, node) {
9301
- const name = localName(key);
9302
- return name === "prstClr" ? parsePresetColor(node) : parseColorElement({ [name]: node });
9303
- }
9304
- function isXmlNode(value) {
9305
- return typeof value === "object" && value !== null;
9306
- }
9307
- function parsePresetColor(node) {
9308
- const value = getAttr(node, "val");
9309
- const hex = value !== void 0 ? PRESET_COLOR_HEX[value] : void 0;
9310
- return hex !== void 0 ? withTransforms({ kind: "srgb", hex }, node) : void 0;
10183
+ Object.assign(next, patch.set);
10184
+ return Object.keys(next).length > 0 ? next : void 0;
9311
10185
  }
9312
- function parseFill(parent, nextId) {
9313
- if (!parent) return void 0;
9314
- const solid = getChild(parent, "solidFill");
9315
- if (solid) {
9316
- const color = parseColorElement(solid);
9317
- if (color) return { kind: "solid", color };
9318
- return { kind: "raw", raw: makeSidecar("a:solidFill", solid, nextId) };
9319
- }
9320
- if (hasChild(parent, "noFill")) return { kind: "none" };
9321
- const grad = getChild(parent, "gradFill");
9322
- if (grad) {
9323
- const fill = parseGradientFill(grad);
9324
- return fill ?? { kind: "raw", raw: makeSidecar("a:gradFill", grad, nextId) };
9325
- }
9326
- const blip = getChild(parent, "blipFill");
9327
- if (blip) {
9328
- return parseBlipFill(blip) ?? { kind: "raw", raw: makeSidecar("a:blipFill", blip, nextId) };
10186
+ function patchParagraphProperties(current, patch) {
10187
+ const next = { ...current ?? {} };
10188
+ for (const property of patch.clear) {
10189
+ delete next[property];
9329
10190
  }
9330
- const pattern = getChild(parent, "pattFill");
9331
- if (pattern) {
9332
- const fill = parsePatternFill(pattern);
9333
- return fill ?? { kind: "raw", raw: makeSidecar("a:pattFill", pattern, nextId) };
10191
+ Object.assign(next, patch.set);
10192
+ return Object.keys(next).length > 0 ? next : void 0;
10193
+ }
10194
+ function assertEditableTextRunProperties(properties) {
10195
+ for (const property of Object.keys(properties)) {
10196
+ assertEditableTextRunPropertyName(property);
9334
10197
  }
9335
- for (const name of RAW_FILL_LOCAL_NAMES) {
9336
- if (hasChild(parent, name)) {
9337
- return { kind: "raw", raw: makeSidecar(`a:${name}`, getChild(parent, name) ?? {}, nextId) };
9338
- }
10198
+ for (const property of EDITABLE_TEXT_RUN_PROPERTIES) {
10199
+ validateEditableTextRunProperty(property, properties[property]);
9339
10200
  }
9340
- return void 0;
9341
10201
  }
9342
- function parseGradientFill(grad) {
9343
- const stops = [];
9344
- for (const gs of getChildArray(getChild(grad, "gsLst"), "gs")) {
9345
- const color = parseColorElement(gs);
9346
- if (color === void 0) continue;
9347
- stops.push({
9348
- position: (numericAttr2(gs, "pos") ?? 0) / 1e5,
9349
- color
9350
- });
10202
+ function assertEditableParagraphProperties(properties) {
10203
+ for (const property of Object.keys(properties)) {
10204
+ assertEditableParagraphPropertyName(property);
9351
10205
  }
9352
- if (stops.length === 0) return void 0;
9353
- const path = getChild(grad, "path");
9354
- if (path !== void 0) {
9355
- const fillToRect = getChild(path, "fillToRect");
9356
- const l = numericAttr2(fillToRect, "l") ?? 0;
9357
- const t = numericAttr2(fillToRect, "t") ?? 0;
9358
- const r = numericAttr2(fillToRect, "r") ?? 0;
9359
- const b = numericAttr2(fillToRect, "b") ?? 0;
9360
- return {
9361
- kind: "gradient",
9362
- gradientType: "radial",
9363
- stops,
9364
- centerX: (l + (1e5 - r)) / 2 / 1e5,
9365
- centerY: (t + (1e5 - b)) / 2 / 1e5
9366
- };
10206
+ validateParagraphAlign(properties.align);
10207
+ validateParagraphLevel(properties.level);
10208
+ validateParagraphBullet(properties.bullet);
10209
+ }
10210
+ function requireBooleanOrUndefined(value, fieldName) {
10211
+ if (value !== void 0 && typeof value !== "boolean") {
10212
+ throw new Error(`updateTextRunProperties: ${fieldName} must be a boolean value`);
9367
10213
  }
9368
- return {
9369
- kind: "gradient",
9370
- gradientType: "linear",
9371
- stops,
9372
- angle: asOoxmlAngle(numericAttr2(getChild(grad, "lin"), "ang") ?? 0)
9373
- };
9374
10214
  }
9375
- function parseBlipFill(blipFill) {
9376
- const blip = getChild(blipFill, "blip");
9377
- const embed = getNamespacedAttr(blip, "embed");
9378
- if (embed === void 0) return void 0;
9379
- const tile = parseImageFillTile(getChild(blipFill, "tile"));
9380
- return {
9381
- kind: "image",
9382
- blipRelationshipId: asRelationshipId(embed),
9383
- ...tile !== void 0 ? { tile } : {}
9384
- };
10215
+ function assertEditableTextRunPropertyNames(properties) {
10216
+ for (const property of properties) {
10217
+ assertEditableTextRunPropertyName(property);
10218
+ }
9385
10219
  }
9386
- function parseImageFillTile(tile) {
9387
- if (tile === void 0) return void 0;
9388
- const flip = getAttr(tile, "flip") ?? "none";
9389
- return {
9390
- tx: asEmu2(numericAttr2(tile, "tx") ?? 0),
9391
- ty: asEmu2(numericAttr2(tile, "ty") ?? 0),
9392
- sx: (numericAttr2(tile, "sx") ?? 1e5) / 1e5,
9393
- sy: (numericAttr2(tile, "sy") ?? 1e5) / 1e5,
9394
- flip: flip === "x" || flip === "y" || flip === "xy" ? flip : "none",
9395
- align: parseRectangleAlignment(getAttr(tile, "algn"), "tl")
9396
- };
10220
+ function assertEditableParagraphPropertyNames(properties) {
10221
+ for (const property of properties) {
10222
+ assertEditableParagraphPropertyName(property);
10223
+ }
9397
10224
  }
9398
- function parseRectangleAlignment(value, fallback) {
9399
- return parseEnumValueWithDefault(value, RECTANGLE_ALIGNMENT_TOKENS, fallback);
10225
+ function definedEditableTextRunProperties(properties) {
10226
+ const defined = {};
10227
+ for (const property of EDITABLE_TEXT_RUN_PROPERTIES) {
10228
+ copyDefinedEditableTextRunProperty(defined, properties, property);
10229
+ }
10230
+ return defined;
9400
10231
  }
9401
- function parsePatternFill(pattern) {
9402
- const foregroundColor = parseColorElement(getChild(pattern, "fgClr"));
9403
- const backgroundColor = parseColorElement(getChild(pattern, "bgClr"));
9404
- if (foregroundColor === void 0 || backgroundColor === void 0) return void 0;
9405
- return {
9406
- kind: "pattern",
9407
- preset: getAttr(pattern, "prst") ?? "ltDnDiag",
9408
- foregroundColor,
9409
- backgroundColor
9410
- };
10232
+ function definedEditableParagraphProperties(properties) {
10233
+ const defined = {};
10234
+ for (const property of EDITABLE_PARAGRAPH_PROPERTIES) {
10235
+ copyDefinedEditableParagraphProperty(defined, properties, property);
10236
+ }
10237
+ return defined;
9411
10238
  }
9412
- function parseOutline(spPr, nextId) {
9413
- const ln = getChild(spPr, "ln");
9414
- return parseLine(ln, nextId);
10239
+ function assertEditableTextRunPropertyName(property) {
10240
+ if (!isEditableTextRunProperty(property)) {
10241
+ throw new Error(`updateTextRunProperties: unsupported text run property '${property}'`);
10242
+ }
9415
10243
  }
9416
- function parseLine(ln, nextId) {
9417
- if (!ln) return void 0;
9418
- const width = numericAttr2(ln, "w");
9419
- const fill = parseFill(ln, nextId);
9420
- const dashStyle = parseDashStyle(getChild(ln, "prstDash"));
9421
- const customDash = parseCustomDash(ln);
9422
- const lineCap = parseLineCap(getAttr(ln, "cap"));
9423
- const lineJoin = parseLineJoin(ln);
9424
- const headEnd = parseArrowEndpoint(getChild(ln, "headEnd"));
9425
- const tailEnd = parseArrowEndpoint(getChild(ln, "tailEnd"));
9426
- return {
9427
- ...width !== void 0 ? { width: asEmu2(width) } : {},
9428
- ...fill !== void 0 ? { fill } : {},
9429
- ...dashStyle !== void 0 ? { dashStyle } : {},
9430
- ...customDash !== void 0 ? { customDash } : {},
9431
- ...lineCap !== void 0 ? { lineCap } : {},
9432
- ...lineJoin !== void 0 ? { lineJoin } : {},
9433
- ...headEnd !== void 0 ? { headEnd } : {},
9434
- ...tailEnd !== void 0 ? { tailEnd } : {}
9435
- };
10244
+ function assertEditableParagraphPropertyName(property) {
10245
+ if (!EDITABLE_PARAGRAPH_PROPERTY_SET.has(property)) {
10246
+ throw new Error(`updateParagraphProperties: unsupported paragraph property '${property}'`);
10247
+ }
9436
10248
  }
9437
- function parseTransform(spPr) {
9438
- const xfrm = getChild(spPr, "xfrm");
9439
- if (!xfrm) return void 0;
9440
- const off = getChild(xfrm, "off");
9441
- const ext = getChild(xfrm, "ext");
9442
- const offsetX = numericAttr2(off, "x");
9443
- const offsetY = numericAttr2(off, "y");
9444
- const width = numericAttr2(ext, "cx");
9445
- const height = numericAttr2(ext, "cy");
9446
- if (offsetX === void 0 || offsetY === void 0 || width === void 0 || height === void 0) {
9447
- return void 0;
10249
+ function isEditableTextRunProperty(property) {
10250
+ return EDITABLE_TEXT_RUN_PROPERTY_SET.has(property);
10251
+ }
10252
+ function validateParagraphAlign(value) {
10253
+ if (value !== void 0 && !TEXT_ALIGN_VALUES.has(value)) {
10254
+ throw new Error("updateParagraphProperties: align must be left, center, right, or justify");
9448
10255
  }
9449
- const rotation = numericAttr2(xfrm, "rot");
9450
- const flipH = getAttr(xfrm, "flipH");
9451
- const flipV = getAttr(xfrm, "flipV");
9452
- return {
9453
- offsetX: asEmu2(offsetX),
9454
- offsetY: asEmu2(offsetY),
9455
- width: asEmu2(width),
9456
- height: asEmu2(height),
9457
- ...rotation !== void 0 ? { rotation: asOoxmlAngle(rotation) } : {},
9458
- ...isTrue(flipH) ? { flipHorizontal: true } : {},
9459
- ...isTrue(flipV) ? { flipVertical: true } : {}
9460
- };
9461
10256
  }
9462
- function withTransforms(base, colorNode) {
9463
- const transforms = [];
9464
- for (const key of Object.keys(colorNode)) {
9465
- if (key.startsWith("@_")) continue;
9466
- const kind = localName(key);
9467
- const transformKind = parseEnumValue(kind, COLOR_TRANSFORM_KINDS);
9468
- if (transformKind === void 0) continue;
9469
- const value = colorNode[key];
9470
- const items = Array.isArray(value) ? value : [value];
9471
- for (const item of items) {
9472
- const raw = getAttr(unsafeOoxmlBoundaryAssertion(item), "val");
9473
- if (raw === void 0) continue;
9474
- const numeric = Number(raw);
9475
- if (!Number.isFinite(numeric)) continue;
9476
- transforms.push({
9477
- kind: transformKind,
9478
- value: asOoxmlPercent(numeric)
9479
- });
10257
+ function validateParagraphLevel(value) {
10258
+ if (value !== void 0 && (!Number.isInteger(value) || value < 0 || value > 8)) {
10259
+ throw new Error("updateParagraphProperties: level must be an integer from 0 to 8");
10260
+ }
10261
+ }
10262
+ function validateParagraphBullet(value) {
10263
+ if (value === void 0) return;
10264
+ if (value.type === "none") return;
10265
+ if (value.type === "char") {
10266
+ if (typeof value.char !== "string" || value.char.length === 0) {
10267
+ throw new Error("updateParagraphProperties: bullet.char must be a non-empty string");
9480
10268
  }
10269
+ return;
9481
10270
  }
9482
- return transforms.length > 0 ? { ...base, transforms } : base;
10271
+ if (value.type === "autoNum") {
10272
+ if (!AUTO_NUM_SCHEMES.has(value.scheme)) {
10273
+ throw new Error("updateParagraphProperties: unsupported bullet auto-numbering scheme");
10274
+ }
10275
+ if (!Number.isInteger(value.startAt) || value.startAt < 1) {
10276
+ throw new Error("updateParagraphProperties: bullet.startAt must be a positive integer");
10277
+ }
10278
+ return;
10279
+ }
10280
+ throw new Error("updateParagraphProperties: unsupported bullet type");
9483
10281
  }
9484
- function numericAttr2(node, name) {
9485
- const raw = getAttr(node, name);
9486
- if (raw === void 0) return void 0;
9487
- const value = Number(raw);
9488
- return Number.isFinite(value) ? value : void 0;
10282
+ function validateEditableTextRunProperty(property, value) {
10283
+ EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS[property](value);
9489
10284
  }
9490
- function isTrue(value) {
9491
- return value === "1" || value === "true";
10285
+ function copyDefinedEditableParagraphProperty(target, source, property) {
10286
+ const value = source[property];
10287
+ if (value !== void 0) {
10288
+ target[property] = value;
10289
+ }
9492
10290
  }
9493
- function parseDashStyle(prstDash) {
9494
- const value = getAttr(prstDash, "val");
9495
- return parseEnumValue(value, DASH_STYLES);
10291
+ function copyDefinedEditableTextRunProperty(target, source, property) {
10292
+ const value = source[property];
10293
+ if (value !== void 0) {
10294
+ target[property] = value;
10295
+ }
9496
10296
  }
9497
- function parseCustomDash(ln) {
9498
- const custDash = getChild(ln, "custDash");
9499
- const segments = getChildArray(custDash, "ds");
9500
- if (segments.length === 0) return void 0;
9501
- return segments.flatMap((segment) => [
9502
- (numericAttr2(segment, "d") ?? 1e5) / 1e5,
9503
- (numericAttr2(segment, "sp") ?? 1e5) / 1e5
9504
- ]);
10297
+ function textRunPropertiesEqual(left, right) {
10298
+ return stableValueEqual2(left ?? {}, right ?? {});
9505
10299
  }
9506
- var LINE_CAP_MAP = {
9507
- flat: "butt",
9508
- sq: "square",
9509
- rnd: "round"
9510
- };
9511
- function parseLineCap(value) {
9512
- return value !== void 0 ? LINE_CAP_MAP[value] : void 0;
10300
+ function paragraphPropertiesEqual(left, right) {
10301
+ return stableValueEqual2(left ?? {}, right ?? {});
9513
10302
  }
9514
- function parseLineJoin(ln) {
9515
- if (hasChild(ln, "round")) return "round";
9516
- if (hasChild(ln, "bevel")) return "bevel";
9517
- if (hasChild(ln, "miter")) return "miter";
9518
- return void 0;
10303
+ function stableValueEqual2(left, right) {
10304
+ if (Object.is(left, right)) return true;
10305
+ if (Array.isArray(left) || Array.isArray(right)) {
10306
+ if (!Array.isArray(left) || !Array.isArray(right)) return false;
10307
+ if (left.length !== right.length) return false;
10308
+ return left.every((value, index) => stableValueEqual2(value, right[index]));
10309
+ }
10310
+ if (isPlainRecord2(left) || isPlainRecord2(right)) {
10311
+ if (!isPlainRecord2(left) || !isPlainRecord2(right)) return false;
10312
+ const leftKeys = Object.keys(left).sort();
10313
+ const rightKeys = Object.keys(right).sort();
10314
+ if (!stableValueEqual2(leftKeys, rightKeys)) return false;
10315
+ return leftKeys.every((key) => stableValueEqual2(left[key], right[key]));
10316
+ }
10317
+ return false;
9519
10318
  }
9520
- function parseArrowEndpoint(node) {
9521
- if (!node) return void 0;
9522
- const type = parseEnumValue(getAttr(node, "type"), ARROW_TYPES2);
9523
- if (type === void 0) return void 0;
9524
- const width = getAttr(node, "w") ?? "med";
9525
- const length = getAttr(node, "len") ?? "med";
10319
+ function isPlainRecord2(value) {
10320
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10321
+ }
10322
+ function createReplacementRunHandle(paragraph) {
10323
+ if (paragraph.runs[0]?.handle !== void 0) return paragraph.runs[0].handle;
10324
+ if (paragraph.handle?.nodeId === void 0) return void 0;
9526
10325
  return {
9527
- type,
9528
- width: parseEnumValueWithDefault(width, ARROW_SIZES2, "med"),
9529
- length: parseEnumValueWithDefault(length, ARROW_SIZES2, "med")
10326
+ ...paragraph.handle,
10327
+ nodeId: asSourceNodeId(`${paragraph.handle.nodeId}:r:0`),
10328
+ orderingSlot: 0
9530
10329
  };
9531
10330
  }
9532
- var DASH_STYLES = /* @__PURE__ */ new Set([
9533
- "solid",
9534
- "dash",
9535
- "dot",
9536
- "dashDot",
9537
- "lgDash",
9538
- "lgDashDot",
9539
- "sysDash",
9540
- "sysDot"
9541
- ]);
9542
- var ARROW_TYPES2 = /* @__PURE__ */ new Set([
9543
- "triangle",
9544
- "stealth",
9545
- "diamond",
9546
- "oval",
9547
- "arrow"
9548
- ]);
9549
- var ARROW_SIZES2 = /* @__PURE__ */ new Set(["sm", "med", "lg"]);
10331
+ function asEmu2(value) {
10332
+ return unsafeBrandAssertion2(value);
10333
+ }
10334
+ function asPt(value) {
10335
+ return unsafeBrandAssertion2(value);
10336
+ }
10337
+ function asHundredthPt2(value) {
10338
+ return unsafeBrandAssertion2(value);
10339
+ }
10340
+ function asOoxmlPercent(value) {
10341
+ return unsafeBrandAssertion2(value);
10342
+ }
10343
+ function asOoxmlAngle(value) {
10344
+ return unsafeBrandAssertion2(value);
10345
+ }
10346
+ var textEncoder = new TextEncoder();
10347
+ var ROOT_PART = asPartPath("");
10348
+ var PRESENTATION_PART = asPartPath("ppt/presentation.xml");
10349
+ var SLIDE_PART = asPartPath("ppt/slides/slide1.xml");
10350
+ var SLIDE_LAYOUT_PART = asPartPath("ppt/slideLayouts/slideLayout1.xml");
10351
+ var SLIDE_MASTER_PART = asPartPath("ppt/slideMasters/slideMaster1.xml");
10352
+ var THEME_PART = asPartPath("ppt/theme/theme1.xml");
10353
+ var APP_PROPS_PART = asPartPath("docProps/app.xml");
10354
+ var CORE_PROPS_PART = asPartPath("docProps/core.xml");
9550
10355
  var DEFAULT_COLOR_MAP = {
9551
10356
  bg1: "lt1",
9552
10357
  tx1: "dk1",
@@ -10110,7 +10915,7 @@ function computeConnectorElement(context, connector, layer, partPath) {
10110
10915
  sourceNode: connector,
10111
10916
  ...connector.transform !== void 0 ? { transform: connector.transform } : {},
10112
10917
  ...connector.geometry !== void 0 ? { geometry: connector.geometry } : {},
10113
- ...connector.outline !== void 0 ? { outline: computeOutline(context, connector.outline, partPath) } : connector.style?.lineRef !== void 0 ? { outline: resolveLineReference(context, connector.style.lineRef, partPath) } : {},
10918
+ ...computedOutlineProperty(context, connector.outline, connector.style?.lineRef, partPath),
10114
10919
  ...effects !== void 0 ? { effects } : {}
10115
10920
  };
10116
10921
  }
@@ -10154,7 +10959,7 @@ function computeShapeElement(context, shape, layer, partPath) {
10154
10959
  ...transform !== void 0 ? { transform } : {},
10155
10960
  ...geometry !== void 0 ? { geometry } : {},
10156
10961
  ...shape.fill !== void 0 ? { fill: computeFill(context, shape.fill, partPath) } : shape.style?.fillRef !== void 0 ? { fill: resolveFillReference(context, shape.style.fillRef, partPath) } : {},
10157
- ...shape.outline !== void 0 ? { outline: computeOutline(context, shape.outline, partPath) } : shape.style?.lineRef !== void 0 ? { outline: resolveLineReference(context, shape.style.lineRef, partPath) } : {},
10962
+ ...computedOutlineProperty(context, shape.outline, shape.style?.lineRef, partPath),
10158
10963
  ...effects !== void 0 ? { effects } : {},
10159
10964
  ...shape.textBody !== void 0 ? {
10160
10965
  textBody: computeTextBody(
@@ -10169,7 +10974,7 @@ function computeShapeElement(context, shape, layer, partPath) {
10169
10974
  }
10170
10975
  function computeImageElement(context, image, layer, partPath) {
10171
10976
  const relationship = resolveComputedRelationships(context.source, partPath).find(
10172
- (rel) => rel.id === image.blipRelationshipId && rel.type === IMAGE_REL_TYPE2
10977
+ (rel2) => rel2.id === image.blipRelationshipId && rel2.type === IMAGE_REL_TYPE2
10173
10978
  );
10174
10979
  const effects = image.effects !== void 0 ? computeEffectList(context, image.effects) : void 0;
10175
10980
  const blipEffects = image.blipEffects !== void 0 ? computeBlipEffects(context, image.blipEffects) : void 0;
@@ -10200,7 +11005,7 @@ function computeTableElement(context, table, layer, partPath) {
10200
11005
  }
10201
11006
  function computeChartElement(context, chart, layer, partPath) {
10202
11007
  const relationship = context.relationships.find(
10203
- (rel) => rel.id === chart.chartRelationshipId && CHART_REL_TYPES.has(rel.type)
11008
+ (rel2) => rel2.id === chart.chartRelationshipId && CHART_REL_TYPES.has(rel2.type)
10204
11009
  );
10205
11010
  const chartXml = relationship?.targetPartPath !== void 0 ? readRawPackageText(context.source, relationship.targetPartPath) : void 0;
10206
11011
  const chartData = chartXml !== void 0 ? parseComputedChartData(chartXml, context) : void 0;
@@ -10217,11 +11022,11 @@ function computeChartElement(context, chart, layer, partPath) {
10217
11022
  }
10218
11023
  function computeSmartArtElement(context, smartArt, layer, partPath) {
10219
11024
  const dataRelationship = context.relationships.find(
10220
- (rel) => rel.id === smartArt.dataRelationshipId && DIAGRAM_DATA_REL_TYPES.has(rel.type)
11025
+ (rel2) => rel2.id === smartArt.dataRelationshipId && DIAGRAM_DATA_REL_TYPES.has(rel2.type)
10221
11026
  );
10222
11027
  const dataRelationships = dataRelationship?.targetPartPath !== void 0 ? resolveComputedRelationships(context.source, dataRelationship.targetPartPath) : [];
10223
11028
  const drawingRelationship = dataRelationships.find(
10224
- (rel) => DIAGRAM_DRAWING_REL_TYPES.has(rel.type)
11029
+ (rel2) => DIAGRAM_DRAWING_REL_TYPES.has(rel2.type)
10225
11030
  );
10226
11031
  const drawingPartPath = drawingRelationship?.targetPartPath;
10227
11032
  const drawingXml = drawingPartPath !== void 0 ? readRawPackageText(context.source, drawingPartPath) : void 0;
@@ -10367,7 +11172,7 @@ function computeFill(context, fill, partPath) {
10367
11172
  };
10368
11173
  case "image": {
10369
11174
  const relationship = resolveComputedRelationships(context.source, partPath).find(
10370
- (rel) => rel.id === fill.blipRelationshipId && rel.type === IMAGE_REL_TYPE2
11175
+ (rel2) => rel2.id === fill.blipRelationshipId && rel2.type === IMAGE_REL_TYPE2
10371
11176
  );
10372
11177
  return {
10373
11178
  kind: "image",
@@ -10392,6 +11197,24 @@ function computeOutline(context, outline, partPath) {
10392
11197
  ...outline.fill !== void 0 ? { fill: computeFill(context, outline.fill, partPath) } : {}
10393
11198
  };
10394
11199
  }
11200
+ function computedOutlineProperty(context, outline, lineRef, partPath) {
11201
+ const styleOutline = lineRef !== void 0 ? resolveLineReference(context, lineRef, partPath) : void 0;
11202
+ if (outline === void 0) {
11203
+ return styleOutline !== void 0 ? { outline: styleOutline } : {};
11204
+ }
11205
+ const computed = mergeComputedOutline(styleOutline, computeOutline(context, outline, partPath));
11206
+ return { outline: computed };
11207
+ }
11208
+ function mergeComputedOutline(base, override) {
11209
+ if (base === void 0) return override;
11210
+ const width = override.width ?? base.width;
11211
+ const fill = override.fill ?? base.fill;
11212
+ return {
11213
+ source: { ...base.source, ...override.source },
11214
+ ...width !== void 0 ? { width } : {},
11215
+ ...fill !== void 0 ? { fill } : {}
11216
+ };
11217
+ }
10395
11218
  function resolveFillReference(context, ref, partPath) {
10396
11219
  if (ref.index === 0) return void 0;
10397
11220
  const list = ref.index >= 1e3 ? context.theme?.formatScheme?.backgroundFillStyles : context.theme?.formatScheme?.fillStyles;
@@ -10980,7 +11803,7 @@ var SLIDE_REL_TYPE2 = "http://schemas.openxmlformats.org/officeDocument/2006/rel
10980
11803
  var SLIDE_LAYOUT_REL_TYPE2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
10981
11804
  var SLIDE_MASTER_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
10982
11805
  var THEME_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
10983
- var PRESENTATION_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml";
11806
+ var PRESENTATION_CONTENT_TYPE2 = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml";
10984
11807
  var textDecoder3 = new TextDecoder();
10985
11808
  function readPptx(input) {
10986
11809
  const entries = unzipPackage(input);
@@ -11147,15 +11970,15 @@ function parsePartRoot(entries, partPath, rootLocalName, diagnostics, includeOrd
11147
11970
  return { root, orderedRoot };
11148
11971
  }
11149
11972
  function resolveSingleRel(relationships, sourcePart, relType) {
11150
- const rels = relationships.find((rel) => rel.sourcePartPath === sourcePart)?.relationships;
11151
- const match = rels?.find((rel) => rel.type === relType && rel.targetMode !== "External");
11973
+ const rels = relationships.find((rel2) => rel2.sourcePartPath === sourcePart)?.relationships;
11974
+ const match = rels?.find((rel2) => rel2.type === relType && rel2.targetMode !== "External");
11152
11975
  if (match === void 0) return void 0;
11153
11976
  return resolveInternalRelationshipTarget(sourcePart, match);
11154
11977
  }
11155
11978
  function resolveAllRels(relationships, sourcePart, relType) {
11156
- const rels = relationships.find((rel) => rel.sourcePartPath === sourcePart)?.relationships ?? [];
11157
- return rels.filter((rel) => rel.type === relType && rel.targetMode !== "External").flatMap((rel) => {
11158
- const target = resolveInternalRelationshipTarget(sourcePart, rel);
11979
+ const rels = relationships.find((rel2) => rel2.sourcePartPath === sourcePart)?.relationships ?? [];
11980
+ return rels.filter((rel2) => rel2.type === relType && rel2.targetMode !== "External").flatMap((rel2) => {
11981
+ const target = resolveInternalRelationshipTarget(sourcePart, rel2);
11159
11982
  return target === void 0 ? [] : [target];
11160
11983
  });
11161
11984
  }
@@ -11244,7 +12067,7 @@ function readPresentation(entries, relationships, overrides, diagnostics) {
11244
12067
  const slideSize = readSlideSize(root);
11245
12068
  const defaultTextStyle = parseTextStyle(getChild(root, "defaultTextStyle"));
11246
12069
  const presentationRels = relationships.find(
11247
- (rel) => rel.sourcePartPath === presentationPath
12070
+ (rel2) => rel2.sourcePartPath === presentationPath
11248
12071
  )?.relationships;
11249
12072
  const slidePartPaths = [];
11250
12073
  const sldIdLst = getChild(root, "sldIdLst");
@@ -11252,7 +12075,7 @@ function readPresentation(entries, relationships, overrides, diagnostics) {
11252
12075
  const relId = getNamespacedAttr(sldId, "id");
11253
12076
  if (relId === void 0) continue;
11254
12077
  const handle = { partPath: presentationPartPath, relationshipId: asRelationshipId(relId) };
11255
- const relationship = presentationRels?.find((rel) => rel.id === relId);
12078
+ const relationship = presentationRels?.find((rel2) => rel2.id === relId);
11256
12079
  if (relationship === void 0) {
11257
12080
  diagnostics.push({
11258
12081
  severity: "warning",
@@ -11295,15 +12118,15 @@ function readSlideSize(presentationRoot) {
11295
12118
  }
11296
12119
  function locatePresentationPart(relationships, overrides) {
11297
12120
  const rootRels = relationships.find(
11298
- (rel) => rel.sourcePartPath === PACKAGE_ROOT_PART
12121
+ (rel2) => rel2.sourcePartPath === PACKAGE_ROOT_PART
11299
12122
  )?.relationships;
11300
12123
  const officeDocumentRel = rootRels?.find(
11301
- (rel) => rel.type === OFFICE_DOCUMENT_REL_TYPE && rel.targetMode !== "External"
12124
+ (rel2) => rel2.type === OFFICE_DOCUMENT_REL_TYPE && rel2.targetMode !== "External"
11302
12125
  );
11303
12126
  if (officeDocumentRel !== void 0) {
11304
12127
  return resolveRelationshipTarget(PACKAGE_ROOT_PART, officeDocumentRel.target);
11305
12128
  }
11306
- const override = overrides.find((entry) => entry.contentType === PRESENTATION_CONTENT_TYPE);
12129
+ const override = overrides.find((entry) => entry.contentType === PRESENTATION_CONTENT_TYPE2);
11307
12130
  return override?.partName;
11308
12131
  }
11309
12132
  var MEDIA_CONTENT_TYPE_PREFIXES = ["image/", "audio/", "video/"];
@@ -11331,9 +12154,9 @@ function extensionOf(path) {
11331
12154
  function stripLeadingSlash(path) {
11332
12155
  return path.startsWith("/") ? path.slice(1) : path;
11333
12156
  }
11334
- function parseShapeLocator(handle) {
12157
+ function parseShapeLocator(handle, editName = "shape edit") {
11335
12158
  if (handle.nodeId !== void 0) return { nodeId: String(handle.nodeId) };
11336
- throw new Error("writePptx: shape transform edit requires nodeId in handle");
12159
+ throw new Error(`writePptx: ${editName} requires nodeId in handle`);
11337
12160
  }
11338
12161
  function parseTextRunLocator(nodeId) {
11339
12162
  const value = String(nodeId ?? "");
@@ -11400,19 +12223,21 @@ function locateShapeTreeNode(spTree, locator) {
11400
12223
  function deleteShapeXml(spTree, nodeId) {
11401
12224
  if (spTree === void 0) return false;
11402
12225
  const entry = Object.entries(spTree).find(
11403
- ([key2]) => !key2.startsWith("@_") && localName(key2) === "sp"
12226
+ ([key2, value2]) => !key2.startsWith("@_") && (localName(key2) === "sp" || localName(key2) === "cxnSp") && getShapeTreeNodes(value2).some((shape) => getShapeTreeNodeId(shape) === nodeId)
11404
12227
  );
11405
12228
  if (entry === void 0) return false;
11406
12229
  const [key, value] = entry;
11407
- const shapes = Array.isArray(value) ? unsafeOoxmlBoundaryAssertion(value) : [value];
11408
- const nextShapes = shapes.filter(
11409
- (shape) => getShapeTreeNodeId(unsafeOoxmlBoundaryAssertion(shape)) !== nodeId
11410
- );
12230
+ const shapes = getShapeTreeNodes(value);
12231
+ const nextShapes = shapes.filter((shape) => getShapeTreeNodeId(shape) !== nodeId);
11411
12232
  if (nextShapes.length === shapes.length) return false;
11412
12233
  if (nextShapes.length === 0) delete spTree[key];
11413
12234
  else spTree[key] = Array.isArray(value) ? nextShapes : nextShapes[0];
11414
12235
  return true;
11415
12236
  }
12237
+ function getShapeTreeNodes(value) {
12238
+ const items = Array.isArray(value) ? unsafeOoxmlBoundaryAssertion(value) : [value];
12239
+ return items.map((item) => unsafeOoxmlBoundaryAssertion(item));
12240
+ }
11416
12241
  function getShapeByOrderingSlot(spTree, orderingSlot) {
11417
12242
  if (!spTree) return void 0;
11418
12243
  let currentSlot = 0;
@@ -11551,7 +12376,7 @@ function textRequiresPreserve(text) {
11551
12376
  return text.startsWith(" ") || text.endsWith(" ");
11552
12377
  }
11553
12378
  var XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
11554
- var textEncoder = new TextEncoder();
12379
+ var textEncoder2 = new TextEncoder();
11555
12380
  var textDecoder4 = new TextDecoder();
11556
12381
  var xmlBuilder2 = new import_fast_xml_parser3.XMLBuilder({
11557
12382
  ignoreAttributes: false,
@@ -11560,8 +12385,16 @@ var xmlBuilder2 = new import_fast_xml_parser3.XMLBuilder({
11560
12385
  suppressEmptyNode: true
11561
12386
  });
11562
12387
  function encodeXml(xml) {
11563
- return textEncoder.encode(xml);
12388
+ return textEncoder2.encode(xml);
11564
12389
  }
12390
+ var FILL_CHILD_LOCAL_NAMES = /* @__PURE__ */ new Set([
12391
+ "noFill",
12392
+ "solidFill",
12393
+ "gradFill",
12394
+ "blipFill",
12395
+ "pattFill",
12396
+ "grpFill"
12397
+ ]);
11565
12398
  function serializeDirtyXmlPart(source, partPath, edits) {
11566
12399
  const rawPart = source.packageGraph.rawParts?.find((part) => part.partPath === partPath);
11567
12400
  if (rawPart === void 0) {
@@ -11585,12 +12418,21 @@ function applyDirtyPartEdit(root, edit) {
11585
12418
  case "updateTextRunProperties":
11586
12419
  applyTextRunPropertiesEdit(root, edit);
11587
12420
  return;
12421
+ case "updateParagraphProperties":
12422
+ applyParagraphPropertiesEdit(root, edit);
12423
+ return;
11588
12424
  case "replaceParagraphPlainText":
11589
12425
  applyParagraphTextEdit(root, edit);
11590
12426
  return;
11591
12427
  case "updateShapeTransform":
11592
12428
  applyShapeTransformEdit(root, edit);
11593
12429
  return;
12430
+ case "updateShapeFill":
12431
+ applyShapeFillEdit(root, edit);
12432
+ return;
12433
+ case "updateShapeOutline":
12434
+ applyShapeOutlineEdit(root, edit);
12435
+ return;
11594
12436
  case "addTextBox":
11595
12437
  applyAddTextBoxEdit(root, edit);
11596
12438
  return;
@@ -11603,6 +12445,7 @@ function applyDirtyPartEdit(root, edit) {
11603
12445
  case "replaceImage":
11604
12446
  case "addEmptySlideFromLayout":
11605
12447
  case "duplicateSlide":
12448
+ case "moveSlide":
11606
12449
  case "deleteSlide":
11607
12450
  throw new Error(`writePptx: edit kind '${edit.kind}' does not patch a dirty XML part`);
11608
12451
  }
@@ -11669,6 +12512,34 @@ function applyParagraphTextEdit(root, edit) {
11669
12512
  }
11670
12513
  replaceParagraphRunsWithSingleTextRun(paragraph, edit.text);
11671
12514
  }
12515
+ function applyParagraphPropertiesEdit(root, edit) {
12516
+ assertParagraphPropertiesEdit(edit);
12517
+ const locator = parseParagraphLocator(edit.handle.nodeId);
12518
+ const slide = getChild(root, "sld");
12519
+ const cSld = getChild(slide, "cSld");
12520
+ const spTree = getChild(cSld, "spTree");
12521
+ const shape = locateShape(spTree, locator);
12522
+ const paragraphs = getChildArray(getChild(shape, "txBody"), "p");
12523
+ const target = locateParagraphPropertiesForEdit(paragraphs, locator);
12524
+ if (target === void 0) {
12525
+ throw new Error(
12526
+ `writePptx: paragraph properties handle '${edit.handle.nodeId}' no longer matches source XML`
12527
+ );
12528
+ }
12529
+ const set = edit.set ?? {};
12530
+ const hasSet = hasParagraphPropertiesSetValues(set);
12531
+ const existingParagraphProperties = target.properties;
12532
+ if (existingParagraphProperties === void 0 && !hasSet) return;
12533
+ const pPr = existingParagraphProperties ?? ensureParagraphProperties(target.paragraph);
12534
+ let cleared = false;
12535
+ for (const property of edit.clear ?? []) {
12536
+ cleared = clearParagraphProperty(pPr, property) || cleared;
12537
+ }
12538
+ if (set.align !== void 0) pPr["@_algn"] = paragraphAlignOoxmlValue(set.align);
12539
+ if (set.level !== void 0) pPr["@_lvl"] = String(set.level);
12540
+ if (set.bullet !== void 0) setParagraphBullet(pPr, set.bullet);
12541
+ if (!hasSet && cleared && xmlNodeIsEmpty(pPr)) deleteParagraphProperties(target.paragraph, pPr);
12542
+ }
11672
12543
  function locateTextRun(root, nodeId) {
11673
12544
  const locator = parseTextRunLocator(nodeId);
11674
12545
  const slide = getChild(root, "sld");
@@ -11680,7 +12551,7 @@ function locateTextRun(root, nodeId) {
11680
12551
  return getChildArray(paragraph, "r")[locator.runIndex];
11681
12552
  }
11682
12553
  function applyShapeTransformEdit(root, edit) {
11683
- const locator = parseShapeLocator(edit.handle);
12554
+ const locator = parseShapeLocator(edit.handle, "shape transform edit");
11684
12555
  const slide = getChild(root, "sld");
11685
12556
  const cSld = getChild(slide, "cSld");
11686
12557
  const spTree = getChild(cSld, "spTree");
@@ -11703,6 +12574,22 @@ function applyShapeTransformEdit(root, edit) {
11703
12574
  ext["@_cx"] = String(edit.width);
11704
12575
  ext["@_cy"] = String(edit.height);
11705
12576
  }
12577
+ function applyShapeFillEdit(root, edit) {
12578
+ const shape = locateEditableShapeTreeNode(root, edit.handle, "shape fill edit");
12579
+ if (shape.localName === "cxnSp") {
12580
+ throw new Error(
12581
+ `writePptx: shape fill handle '${String(edit.handle.nodeId)}' references a connector`
12582
+ );
12583
+ }
12584
+ const spPr = ensureShapeProperties(shape.node);
12585
+ replaceFillChild(spPr, edit.fill);
12586
+ }
12587
+ function applyShapeOutlineEdit(root, edit) {
12588
+ const shape = locateEditableShapeTreeNode(root, edit.handle, "shape outline edit");
12589
+ const spPr = ensureShapeProperties(shape.node);
12590
+ const ln = ensureLineProperties(spPr);
12591
+ applyOutlinePatch(ln, edit.outline);
12592
+ }
11706
12593
  function applyAddTextBoxEdit(root, edit) {
11707
12594
  const slide = getChild(root, "sld");
11708
12595
  const cSld = getChild(slide, "cSld");
@@ -11725,22 +12612,133 @@ function applyAddConnectorEdit(root, edit) {
11725
12612
  if (locateShapeTreeNode(spTree, { nodeId: edit.shapeId }) !== void 0) {
11726
12613
  throw new Error(`writePptx: shape id '${edit.shapeId}' already exists in source XML`);
11727
12614
  }
11728
- if (locateShapeTreeNode(spTree, { nodeId: edit.startShapeId }) === void 0) {
12615
+ if (edit.startShapeId !== void 0 && locateShapeTreeNode(spTree, { nodeId: edit.startShapeId }) === void 0) {
11729
12616
  throw new Error(`writePptx: connector start shape '${edit.startShapeId}' was not found`);
11730
12617
  }
11731
- if (locateShapeTreeNode(spTree, { nodeId: edit.endShapeId }) === void 0) {
12618
+ if (edit.endShapeId !== void 0 && locateShapeTreeNode(spTree, { nodeId: edit.endShapeId }) === void 0) {
11732
12619
  throw new Error(`writePptx: connector end shape '${edit.endShapeId}' was not found`);
11733
12620
  }
11734
- appendChild(spTree, "p:cxnSp", parseShapeFragmentXml(edit.xml, "cxnSp"));
12621
+ appendChild(spTree, "p:cxnSp", parseShapeFragmentXml(edit.xml, "cxnSp"));
12622
+ }
12623
+ function applyDeleteShapeEdit(root, edit) {
12624
+ const locator = parseShapeLocator(edit.handle, "shape delete edit");
12625
+ const slide = getChild(root, "sld");
12626
+ const cSld = getChild(slide, "cSld");
12627
+ const spTree = getChild(cSld, "spTree");
12628
+ if (!deleteShapeXml(spTree, locator.nodeId)) {
12629
+ throw new Error(
12630
+ `writePptx: shape delete handle '${locator.nodeId}' no longer matches p:sp or p:cxnSp`
12631
+ );
12632
+ }
12633
+ }
12634
+ function locateEditableShapeTreeNode(root, handle, editName) {
12635
+ const locator = parseShapeLocator(handle, editName);
12636
+ const slide = getChild(root, "sld");
12637
+ const cSld = getChild(slide, "cSld");
12638
+ const spTree = getChild(cSld, "spTree");
12639
+ const shape = locateShapeTreeNodeWithLocalName(spTree, locator.nodeId);
12640
+ if (shape === void 0) {
12641
+ throw new Error(
12642
+ `writePptx: ${editName} handle '${String(handle.nodeId)}' no longer matches source XML`
12643
+ );
12644
+ }
12645
+ if (shape.localName !== "sp" && shape.localName !== "cxnSp") {
12646
+ throw new Error(
12647
+ `writePptx: ${editName} handle '${String(handle.nodeId)}' does not reference p:sp or p:cxnSp`
12648
+ );
12649
+ }
12650
+ return shape;
12651
+ }
12652
+ function locateShapeTreeNodeWithLocalName(spTree, nodeId) {
12653
+ if (spTree === void 0) return void 0;
12654
+ for (const key of Object.keys(spTree)) {
12655
+ if (key.startsWith("@_")) continue;
12656
+ const keyLocalName = localName(key);
12657
+ const value = spTree[key];
12658
+ const items = Array.isArray(value) ? value : [value];
12659
+ for (const item of items) {
12660
+ const node = unsafeOoxmlBoundaryAssertion(item);
12661
+ const nonVisualProperties = getChild(node, "nvSpPr") ?? getChild(node, "nvPicPr") ?? getChild(node, "nvCxnSpPr") ?? getChild(node, "nvGrpSpPr") ?? getChild(node, "nvGraphicFramePr");
12662
+ if (getChild(nonVisualProperties, "cNvPr")?.["@_id"] === nodeId) {
12663
+ return { node, localName: keyLocalName };
12664
+ }
12665
+ }
12666
+ }
12667
+ return void 0;
12668
+ }
12669
+ function ensureShapeProperties(shape) {
12670
+ const existing = getChild(shape, "spPr");
12671
+ if (existing !== void 0) return existing;
12672
+ const entries = [];
12673
+ let inserted = false;
12674
+ for (const [key, value] of Object.entries(shape)) {
12675
+ entries.push([key, value]);
12676
+ if (!inserted && !key.startsWith("@_") && localName(key).startsWith("nv")) {
12677
+ entries.push(["p:spPr", {}]);
12678
+ inserted = true;
12679
+ }
12680
+ }
12681
+ if (!inserted) entries.push(["p:spPr", {}]);
12682
+ replaceNodeEntries(shape, entries);
12683
+ return getChild(shape, "spPr") ?? {};
11735
12684
  }
11736
- function applyDeleteShapeEdit(root, edit) {
11737
- const locator = parseShapeLocator(edit.handle);
11738
- const slide = getChild(root, "sld");
11739
- const cSld = getChild(slide, "cSld");
11740
- const spTree = getChild(cSld, "spTree");
11741
- if (!deleteShapeXml(spTree, locator.nodeId)) {
11742
- throw new Error(`writePptx: shape delete handle '${locator.nodeId}' no longer matches p:sp`);
12685
+ function ensureLineProperties(spPr) {
12686
+ const existing = getChild(spPr, "ln");
12687
+ if (existing !== void 0) return existing;
12688
+ insertChildByOrder(
12689
+ spPr,
12690
+ "a:ln",
12691
+ {},
12692
+ (name) => ["effectLst", "effectDag", "scene3d", "sp3d", "extLst"].includes(name)
12693
+ );
12694
+ return getChild(spPr, "ln") ?? {};
12695
+ }
12696
+ function applyOutlinePatch(ln, outline) {
12697
+ if (outline.width !== void 0) ln["@_w"] = String(outline.width);
12698
+ if (outline.fill !== void 0) replaceFillChild(ln, outline.fill);
12699
+ }
12700
+ function replaceFillChild(parent, fill) {
12701
+ const fillNode = fill.kind === "none" ? { key: "a:noFill", value: {} } : {
12702
+ key: "a:solidFill",
12703
+ value: { "a:srgbClr": { "@_val": fill.color.hex.toUpperCase() } }
12704
+ };
12705
+ const entries = Object.entries(parent).filter(
12706
+ ([key]) => key.startsWith("@_") || !FILL_CHILD_LOCAL_NAMES.has(localName(key))
12707
+ );
12708
+ replaceNodeEntries(parent, entries);
12709
+ insertChildByOrder(
12710
+ parent,
12711
+ fillNode.key,
12712
+ fillNode.value,
12713
+ (name) => [
12714
+ "ln",
12715
+ "effectLst",
12716
+ "effectDag",
12717
+ "scene3d",
12718
+ "sp3d",
12719
+ "extLst",
12720
+ "prstDash",
12721
+ "custDash",
12722
+ "round",
12723
+ "bevel",
12724
+ "miter",
12725
+ "headEnd",
12726
+ "tailEnd"
12727
+ ].includes(name)
12728
+ );
12729
+ }
12730
+ function insertChildByOrder(node, key, value, shouldInsertBefore) {
12731
+ const entries = [];
12732
+ let inserted = false;
12733
+ for (const [entryKey, entryValue] of Object.entries(node)) {
12734
+ if (!inserted && !entryKey.startsWith("@_") && shouldInsertBefore(localName(entryKey))) {
12735
+ entries.push([key, value]);
12736
+ inserted = true;
12737
+ }
12738
+ entries.push([entryKey, entryValue]);
11743
12739
  }
12740
+ if (!inserted) entries.push([key, value]);
12741
+ replaceNodeEntries(node, entries);
11744
12742
  }
11745
12743
  function parseShapeFragmentXml(xml, rootLocalName) {
11746
12744
  const node = getChild(parseXml(xml), rootLocalName);
@@ -11802,12 +12800,104 @@ function booleanOoxmlValue(value) {
11802
12800
  function hasTextRunPropertiesSetValues(properties) {
11803
12801
  return properties.bold !== void 0 || properties.italic !== void 0 || properties.underline !== void 0 || properties.fontSize !== void 0 || properties.color !== void 0 || properties.typeface !== void 0;
11804
12802
  }
12803
+ function hasParagraphPropertiesSetValues(properties) {
12804
+ return properties.align !== void 0 || properties.level !== void 0 || properties.bullet !== void 0;
12805
+ }
11805
12806
  function assertTextRunPropertiesEdit(edit) {
11806
12807
  const clear = edit.clear ?? [];
11807
12808
  if (!hasTextRunPropertiesSetValues(edit.set ?? {}) && clear.length === 0) {
11808
12809
  throw new Error("writePptx: text run properties edit must set or clear at least one property");
11809
12810
  }
11810
12811
  }
12812
+ function assertParagraphPropertiesEdit(edit) {
12813
+ const clear = edit.clear ?? [];
12814
+ if (!hasParagraphPropertiesSetValues(edit.set ?? {}) && clear.length === 0) {
12815
+ throw new Error("writePptx: paragraph properties edit must set or clear at least one property");
12816
+ }
12817
+ }
12818
+ function locateParagraphPropertiesForEdit(paragraphs, locator) {
12819
+ let logicalParagraphIndex = 0;
12820
+ for (const paragraph of paragraphs) {
12821
+ const logicalCount = getLogicalParagraphCount(paragraph);
12822
+ if (locator.paragraphIndex >= logicalParagraphIndex && locator.paragraphIndex < logicalParagraphIndex + logicalCount) {
12823
+ if (logicalCount === 1) {
12824
+ return { paragraph, properties: getChild(paragraph, "pPr") };
12825
+ }
12826
+ const relativeIndex = locator.paragraphIndex - logicalParagraphIndex;
12827
+ return {
12828
+ paragraph,
12829
+ properties: getBulletParagraphProperties(paragraph)[relativeIndex]
12830
+ };
12831
+ }
12832
+ logicalParagraphIndex += logicalCount;
12833
+ }
12834
+ return void 0;
12835
+ }
12836
+ function getBulletParagraphProperties(paragraph) {
12837
+ return getChildArray(paragraph, "pPr").filter(
12838
+ (properties) => getChild(properties, "buChar") !== void 0 || getChild(properties, "buAutoNum") !== void 0
12839
+ );
12840
+ }
12841
+ function ensureParagraphProperties(paragraph) {
12842
+ const existing = getChild(paragraph, "pPr");
12843
+ if (existing !== void 0) return existing;
12844
+ const entries = [];
12845
+ let inserted = false;
12846
+ for (const [key, value] of Object.entries(paragraph)) {
12847
+ if (!inserted && !key.startsWith("@_")) {
12848
+ entries.push(["a:pPr", {}]);
12849
+ inserted = true;
12850
+ }
12851
+ entries.push([key, value]);
12852
+ }
12853
+ if (!inserted) entries.push(["a:pPr", {}]);
12854
+ replaceNodeEntries(paragraph, entries);
12855
+ return getChild(paragraph, "pPr") ?? {};
12856
+ }
12857
+ function deleteParagraphProperties(paragraph, pPr) {
12858
+ if (getChild(paragraph, "pPr") === pPr) deleteChild(paragraph, "pPr");
12859
+ }
12860
+ function clearParagraphProperty(pPr, property) {
12861
+ switch (property) {
12862
+ case "align":
12863
+ if (pPr["@_algn"] === void 0) return false;
12864
+ delete pPr["@_algn"];
12865
+ return true;
12866
+ case "level":
12867
+ if (pPr["@_lvl"] === void 0) return false;
12868
+ delete pPr["@_lvl"];
12869
+ return true;
12870
+ case "bullet":
12871
+ return deleteParagraphBullet(pPr);
12872
+ }
12873
+ }
12874
+ function deleteParagraphBullet(pPr) {
12875
+ const deletedNone = deleteChild(pPr, "buNone");
12876
+ const deletedChar = deleteChild(pPr, "buChar");
12877
+ const deletedAutoNum = deleteChild(pPr, "buAutoNum");
12878
+ return deletedNone || deletedChar || deletedAutoNum;
12879
+ }
12880
+ function setParagraphBullet(pPr, bullet) {
12881
+ deleteParagraphBullet(pPr);
12882
+ if (bullet.type === "none") {
12883
+ replaceChild(pPr, "buNone", {});
12884
+ return;
12885
+ }
12886
+ if (bullet.type === "char") {
12887
+ replaceChild(pPr, "buChar", { "@_char": bullet.char });
12888
+ return;
12889
+ }
12890
+ replaceChild(pPr, "buAutoNum", {
12891
+ "@_type": bullet.scheme,
12892
+ "@_startAt": String(bullet.startAt)
12893
+ });
12894
+ }
12895
+ function paragraphAlignOoxmlValue(align) {
12896
+ if (align === "center") return "ctr";
12897
+ if (align === "right") return "r";
12898
+ if (align === "justify") return "just";
12899
+ return "l";
12900
+ }
11811
12901
  function replaceParagraphRunsWithSingleTextRun(paragraph, text) {
11812
12902
  const firstRunProperties = getChild(getFirstRunLikeNode(paragraph), "rPr");
11813
12903
  const replacementRun = {
@@ -11876,6 +12966,8 @@ function validateEdits(edits) {
11876
12966
  const runKeys = /* @__PURE__ */ new Set();
11877
12967
  const paragraphKeys = /* @__PURE__ */ new Set();
11878
12968
  const shapeKeys = /* @__PURE__ */ new Set();
12969
+ const shapeFillKeys = /* @__PURE__ */ new Set();
12970
+ const shapeOutlineKeys = /* @__PURE__ */ new Set();
11879
12971
  const deletedShapeKeys = /* @__PURE__ */ new Set();
11880
12972
  const textRunEdits = [];
11881
12973
  const textRunPropertiesEdits = [];
@@ -11904,6 +12996,8 @@ function validateEdits(edits) {
11904
12996
  case "updateTextRunProperties":
11905
12997
  textRunPropertiesEdits.push(edit);
11906
12998
  break;
12999
+ case "updateParagraphProperties":
13000
+ break;
11907
13001
  case "replaceParagraphPlainText": {
11908
13002
  const key = editHandleNodeKey(edit);
11909
13003
  if (paragraphKeys.has(key)) {
@@ -11924,6 +13018,26 @@ function validateEdits(edits) {
11924
13018
  shapeKeys.add(key);
11925
13019
  break;
11926
13020
  }
13021
+ case "updateShapeFill": {
13022
+ const key = editHandleNodeKey(edit);
13023
+ if (shapeFillKeys.has(key)) {
13024
+ throw new Error(
13025
+ `writePptx: conflicting shape fill edits for handle '${String(edit.handle.nodeId)}'`
13026
+ );
13027
+ }
13028
+ shapeFillKeys.add(key);
13029
+ break;
13030
+ }
13031
+ case "updateShapeOutline": {
13032
+ const key = editHandleNodeKey(edit);
13033
+ if (shapeOutlineKeys.has(key)) {
13034
+ throw new Error(
13035
+ `writePptx: conflicting shape outline edits for handle '${String(edit.handle.nodeId)}'`
13036
+ );
13037
+ }
13038
+ shapeOutlineKeys.add(key);
13039
+ break;
13040
+ }
11927
13041
  case "deleteShape": {
11928
13042
  const key = editHandleNodeKey(edit);
11929
13043
  if (deletedShapeKeys.has(key)) {
@@ -11937,6 +13051,7 @@ function validateEdits(edits) {
11937
13051
  case "replaceImage":
11938
13052
  case "addEmptySlideFromLayout":
11939
13053
  case "duplicateSlide":
13054
+ case "moveSlide":
11940
13055
  case "deleteSlide":
11941
13056
  break;
11942
13057
  }
@@ -12043,6 +13158,9 @@ function serializePresentationWithSlideTopologyEdits(source, operations) {
12043
13158
  case "removeSlide":
12044
13159
  removeSlideId(sldIdLst, operation.relationshipId);
12045
13160
  break;
13161
+ case "moveSlide":
13162
+ moveSlideId(sldIdLst, operation.relationshipId, operation.toIndex);
13163
+ break;
12046
13164
  }
12047
13165
  }
12048
13166
  return encodeXml(XML_DECLARATION + xmlBuilder2.build(stripXmlProcessingInstruction(root)));
@@ -12085,6 +13203,24 @@ function removeSlideId(sldIdLst, relationshipId) {
12085
13203
  const { key, items } = slideIdEntries(sldIdLst);
12086
13204
  sldIdLst[key] = items.filter((item) => getRelationshipAttr(item) !== relationshipId);
12087
13205
  }
13206
+ function moveSlideId(sldIdLst, relationshipId, toIndex) {
13207
+ const { key, items } = slideIdEntries(sldIdLst);
13208
+ const fromIndex = items.findIndex((item2) => getRelationshipAttr(item2) === relationshipId);
13209
+ if (fromIndex === -1) {
13210
+ throw new Error(
13211
+ `writePptx: slide relationship '${relationshipId}' was not found in p:sldIdLst`
13212
+ );
13213
+ }
13214
+ if (toIndex < 0 || toIndex >= items.length) {
13215
+ throw new Error(`writePptx: slide move target index '${toIndex}' is out of range`);
13216
+ }
13217
+ if (fromIndex === toIndex) return;
13218
+ const moved = [...items];
13219
+ const [item] = moved.splice(fromIndex, 1);
13220
+ if (item === void 0) return;
13221
+ moved.splice(toIndex, 0, item);
13222
+ sldIdLst[key] = moved;
13223
+ }
12088
13224
  function slideIdEntries(sldIdLst) {
12089
13225
  const key = namespacedChildKey(sldIdLst, "p:sldId", "sldId");
12090
13226
  const value = sldIdLst[key];
@@ -12098,7 +13234,7 @@ function getRelationshipAttr(node) {
12098
13234
  return getNamespacedAttr(node, "id");
12099
13235
  }
12100
13236
  var CONTENT_TYPES_PART2 = "[Content_Types].xml";
12101
- var RELS_CONTENT_TYPE2 = "application/vnd.openxmlformats-package.relationships+xml";
13237
+ var RELS_CONTENT_TYPE3 = "application/vnd.openxmlformats-package.relationships+xml";
12102
13238
  function writePptx(source) {
12103
13239
  const edits = source.edits ?? [];
12104
13240
  validateEdits(edits);
@@ -12145,7 +13281,7 @@ function writePptx(source) {
12145
13281
  }
12146
13282
  for (const part of source.packageGraph.parts) {
12147
13283
  if (written.has(part.partPath)) continue;
12148
- if (part.contentType === RELS_CONTENT_TYPE2 || isRelationshipPart(part.partPath)) continue;
13284
+ if (part.contentType === RELS_CONTENT_TYPE3 || isRelationshipPart(part.partPath)) continue;
12149
13285
  throw new Error(
12150
13286
  "writePptx: no preserved package material for part '" + part.partPath + "'; edited part generation is not implemented in the no-edit writer"
12151
13287
  );
@@ -15498,6 +16634,23 @@ var pptxTextBodySchema = new Schema({
15498
16634
  }
15499
16635
  }
15500
16636
  });
16637
+ var EDITABLE_PARAGRAPH_PROPERTIES2 = [
16638
+ "align",
16639
+ "level",
16640
+ "bullet"
16641
+ ];
16642
+ var AUTO_NUM_SCHEMES2 = [
16643
+ "arabicPeriod",
16644
+ "arabicParenR",
16645
+ "romanUcPeriod",
16646
+ "romanLcPeriod",
16647
+ "alphaUcPeriod",
16648
+ "alphaLcPeriod",
16649
+ "alphaLcParenR",
16650
+ "alphaUcParenR",
16651
+ "arabicPlain"
16652
+ ];
16653
+ var AUTO_NUM_SCHEME_SET = new Set(AUTO_NUM_SCHEMES2);
15501
16654
  function textBodyToProseMirrorDocJson(textBody) {
15502
16655
  assertSupportedTextBody(textBody);
15503
16656
  return {
@@ -15563,8 +16716,12 @@ function proseMirrorDocJsonToEditorCommands(originalTextBody, docJson) {
15563
16716
  (editedParagraph, paragraphIndex) => {
15564
16717
  const originalParagraph = originalTextBody.paragraphs[paragraphIndex];
15565
16718
  if (originalParagraph === void 0) return [];
16719
+ const paragraphPropertyCommands = paragraphPropertiesToEditorCommands(
16720
+ originalParagraph,
16721
+ editedParagraph
16722
+ );
15566
16723
  if (paragraphRunHandlesMatch(originalParagraph, editedParagraph)) {
15567
- return editedParagraph.runs.flatMap(
16724
+ const textRunCommands = editedParagraph.runs.flatMap(
15568
16725
  (editedRun, runIndex) => {
15569
16726
  const originalRun = originalParagraph.runs[runIndex];
15570
16727
  if (originalRun === void 0 || editedRun.text === originalRun.text) return [];
@@ -15582,6 +16739,7 @@ function proseMirrorDocJsonToEditorCommands(originalTextBody, docJson) {
15582
16739
  ];
15583
16740
  }
15584
16741
  );
16742
+ return [...paragraphPropertyCommands, ...textRunCommands];
15585
16743
  }
15586
16744
  if (editedParagraph.handle === void 0) {
15587
16745
  throw new Error(
@@ -15589,6 +16747,7 @@ function proseMirrorDocJsonToEditorCommands(originalTextBody, docJson) {
15589
16747
  );
15590
16748
  }
15591
16749
  return [
16750
+ ...paragraphPropertyCommands,
15592
16751
  {
15593
16752
  kind: "replaceParagraphPlainText",
15594
16753
  handle: editedParagraph.handle,
@@ -15604,10 +16763,14 @@ function validateProseMirrorDocJson(docJson) {
15604
16763
  }
15605
16764
  function paragraphJsonToSourceParagraph(originalParagraph, paragraphJson) {
15606
16765
  const groups = collectRunGroups(paragraphJson, originalParagraph);
16766
+ const properties = paragraphPropertiesFromJson(
16767
+ originalParagraph?.properties,
16768
+ paragraphJson.attrs?.properties
16769
+ );
15607
16770
  return {
15608
- ...originalParagraph ?? {},
15609
16771
  runs: groups.map(sourceRunFromGroup),
15610
- ...originalParagraph?.properties !== void 0 ? { properties: originalParagraph.properties } : {},
16772
+ ...originalParagraph?.rawSidecars !== void 0 ? { rawSidecars: originalParagraph.rawSidecars } : {},
16773
+ ...properties !== void 0 ? { properties } : {},
15611
16774
  ...originalParagraph?.handle !== void 0 ? { handle: originalParagraph.handle } : sourceHandleFromUnknown(paragraphJson.attrs?.handle) !== void 0 ? { handle: sourceHandleFromUnknown(paragraphJson.attrs?.handle) } : {}
15612
16775
  };
15613
16776
  }
@@ -15657,6 +16820,107 @@ function paragraphRunHandlesMatch(originalParagraph, editedParagraph) {
15657
16820
  function paragraphPlainText(paragraph) {
15658
16821
  return paragraph.runs.map((run) => run.text).join("");
15659
16822
  }
16823
+ function paragraphPropertiesToEditorCommands(originalParagraph, editedParagraph) {
16824
+ if (editedParagraph.handle === void 0) {
16825
+ if (paragraphEditablePropertiesEqual(originalParagraph.properties, editedParagraph.properties)) {
16826
+ return [];
16827
+ }
16828
+ throw new Error("proseMirrorDocJsonToEditorCommands: changed paragraph has no source handle");
16829
+ }
16830
+ const set = {};
16831
+ const clear = [];
16832
+ if (!stableValueEqual3(originalParagraph.properties?.align, editedParagraph.properties?.align)) {
16833
+ if (editedParagraph.properties?.align === void 0) clear.push("align");
16834
+ else set.align = editedParagraph.properties.align;
16835
+ }
16836
+ if (!stableValueEqual3(originalParagraph.properties?.level, editedParagraph.properties?.level)) {
16837
+ if (editedParagraph.properties?.level === void 0) clear.push("level");
16838
+ else set.level = editedParagraph.properties.level;
16839
+ }
16840
+ if (!stableValueEqual3(originalParagraph.properties?.bullet, editedParagraph.properties?.bullet)) {
16841
+ if (editedParagraph.properties?.bullet === void 0) clear.push("bullet");
16842
+ else set.bullet = editedParagraph.properties.bullet;
16843
+ }
16844
+ const commands = [];
16845
+ if (clear.length > 0) {
16846
+ commands.push({
16847
+ kind: "clearParagraphProperties",
16848
+ handle: editedParagraph.handle,
16849
+ properties: clear
16850
+ });
16851
+ }
16852
+ if (Object.keys(set).length > 0) {
16853
+ commands.push({
16854
+ kind: "setParagraphProperties",
16855
+ handle: editedParagraph.handle,
16856
+ properties: set
16857
+ });
16858
+ }
16859
+ return commands;
16860
+ }
16861
+ function paragraphPropertiesFromJson(originalProperties, value) {
16862
+ if (!isRecord(value)) return originalProperties;
16863
+ const next = { ...originalProperties ?? {} };
16864
+ for (const property of EDITABLE_PARAGRAPH_PROPERTIES2) {
16865
+ if (!Object.prototype.hasOwnProperty.call(value, property)) {
16866
+ delete next[property];
16867
+ continue;
16868
+ }
16869
+ const propertyValue = value[property];
16870
+ if (propertyValue === null || propertyValue === void 0) {
16871
+ delete next[property];
16872
+ } else if (property === "align") {
16873
+ next.align = paragraphAlignFromUnknown(propertyValue);
16874
+ } else if (property === "level") {
16875
+ next.level = paragraphLevelFromUnknown(propertyValue);
16876
+ } else {
16877
+ next.bullet = paragraphBulletFromUnknown(propertyValue);
16878
+ }
16879
+ }
16880
+ return Object.keys(next).length > 0 ? next : void 0;
16881
+ }
16882
+ function paragraphAlignFromUnknown(value) {
16883
+ if (value === "left" || value === "center" || value === "right" || value === "justify") {
16884
+ return value;
16885
+ }
16886
+ throw new Error("ProseMirror paragraph properties align must be left, center, right, or justify");
16887
+ }
16888
+ function paragraphLevelFromUnknown(value) {
16889
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 8) {
16890
+ return value;
16891
+ }
16892
+ throw new Error("ProseMirror paragraph properties level must be an integer from 0 to 8");
16893
+ }
16894
+ function paragraphBulletFromUnknown(value) {
16895
+ if (!isRecord(value) || typeof value.type !== "string") {
16896
+ throw new Error("ProseMirror paragraph properties bullet must be an object");
16897
+ }
16898
+ if (value.type === "none") return { type: "none" };
16899
+ if (value.type === "char") {
16900
+ if (typeof value.char !== "string" || value.char.length === 0) {
16901
+ throw new Error("ProseMirror paragraph properties bullet.char must be a non-empty string");
16902
+ }
16903
+ return { type: "char", char: value.char };
16904
+ }
16905
+ if (value.type === "autoNum") {
16906
+ if (!isAutoNumScheme(value.scheme)) {
16907
+ throw new Error("ProseMirror paragraph properties bullet.scheme is unsupported");
16908
+ }
16909
+ if (typeof value.startAt !== "number" || !Number.isInteger(value.startAt) || value.startAt < 1) {
16910
+ throw new Error("ProseMirror paragraph properties bullet.startAt must be positive");
16911
+ }
16912
+ return { type: "autoNum", scheme: value.scheme, startAt: value.startAt };
16913
+ }
16914
+ throw new Error("ProseMirror paragraph properties bullet.type is unsupported");
16915
+ }
16916
+ function isAutoNumScheme(value) {
16917
+ return typeof value === "string" && AUTO_NUM_SCHEME_SET.has(value);
16918
+ }
16919
+ function paragraphEditablePropertiesEqual(left, right) {
16920
+ return EDITABLE_PARAGRAPH_PROPERTIES2.every(
16921
+ (property) => stableValueEqual3(left?.[property], right?.[property])
16922
+ );
16923
+ }
15660
16924
  function parsePptxTextBodyProseMirrorDocJson(value) {
15661
16925
  if (!isRecord(value) || value.type !== "doc") {
15662
16926
  throw new Error("ProseMirror text body doc JSON must be a doc node");
@@ -15664,7 +16928,7 @@ function parsePptxTextBodyProseMirrorDocJson(value) {
15664
16928
  if (value.content !== void 0 && readArray(value.content, isParagraphJson) === void 0) {
15665
16929
  throw new Error("ProseMirror text body doc JSON content must contain paragraph nodes");
15666
16930
  }
15667
- const content = readArray(value.content, isParagraphJson);
16931
+ const content = readArray(value.content, isParagraphJson)?.map(normalizeParagraphJson);
15668
16932
  return {
15669
16933
  type: "doc",
15670
16934
  ...content !== void 0 ? { content } : {}
@@ -15677,6 +16941,13 @@ function isParagraphJson(value) {
15677
16941
  }
15678
16942
  return value.attrs === void 0 || isRecord(value.attrs);
15679
16943
  }
16944
+ function normalizeParagraphJson(value) {
16945
+ return {
16946
+ type: "paragraph",
16947
+ ...value.attrs !== void 0 ? { attrs: value.attrs } : {},
16948
+ ...value.content !== void 0 ? { content: value.content } : {}
16949
+ };
16950
+ }
15680
16951
  function isTextJson(value) {
15681
16952
  if (!isRecord(value) || value.type !== "text" || typeof value.text !== "string") return false;
15682
16953
  if (value.marks === void 0) return true;
@@ -15714,6 +16985,22 @@ function sourceHandleKey(handle) {
15714
16985
  handle.orderingSlot ?? ""
15715
16986
  ].join("\0");
15716
16987
  }
16988
+ function stableValueEqual3(left, right) {
16989
+ if (Object.is(left, right)) return true;
16990
+ if (Array.isArray(left) || Array.isArray(right)) {
16991
+ if (!Array.isArray(left) || !Array.isArray(right)) return false;
16992
+ if (left.length !== right.length) return false;
16993
+ return left.every((value, index) => stableValueEqual3(value, right[index]));
16994
+ }
16995
+ if (isRecord(left) || isRecord(right)) {
16996
+ if (!isRecord(left) || !isRecord(right)) return false;
16997
+ const leftKeys = Object.keys(left).sort();
16998
+ const rightKeys = Object.keys(right).sort();
16999
+ if (!stableValueEqual3(leftKeys, rightKeys)) return false;
17000
+ return leftKeys.every((key) => stableValueEqual3(left[key], right[key]));
17001
+ }
17002
+ return false;
17003
+ }
15717
17004
  function isRecord(value) {
15718
17005
  return typeof value === "object" && value !== null && !Array.isArray(value);
15719
17006
  }
@@ -15827,12 +17114,20 @@ function applyCommandToDocument(document, command) {
15827
17114
  return setTextRunPropertiesCommand(document, command);
15828
17115
  case "clearTextRunProperties":
15829
17116
  return clearTextRunPropertiesCommand(document, command);
17117
+ case "setParagraphProperties":
17118
+ return setParagraphPropertiesCommand(document, command);
17119
+ case "clearParagraphProperties":
17120
+ return clearParagraphPropertiesCommand(document, command);
15830
17121
  case "moveShape":
15831
17122
  return moveShape(document, command);
15832
17123
  case "resizeShape":
15833
17124
  return resizeShape(document, command);
15834
17125
  case "setShapeTransform":
15835
17126
  return setShapeTransform(document, command);
17127
+ case "setShapeFill":
17128
+ return setShapeFillCommand(document, command);
17129
+ case "setShapeOutline":
17130
+ return setShapeOutlineCommand(document, command);
15836
17131
  case "addTextBox":
15837
17132
  return addTextBoxCommand(document, command);
15838
17133
  case "addConnector":
@@ -15845,6 +17140,8 @@ function applyCommandToDocument(document, command) {
15845
17140
  return addEmptySlideFromLayout(document, command);
15846
17141
  case "duplicateSlide":
15847
17142
  return duplicateSlide(document, command.handle);
17143
+ case "moveSlide":
17144
+ return moveSlide(document, command.handle, command);
15848
17145
  case "deleteSlide":
15849
17146
  return deleteSlide(document, command.handle);
15850
17147
  }
@@ -15886,7 +17183,7 @@ function addTextBoxCommand(document, command) {
15886
17183
  requireFiniteEmu(command.offsetY, "addTextBox", "offsetY");
15887
17184
  requirePositiveFiniteEmu(command.width, "addTextBox", "width");
15888
17185
  requirePositiveFiniteEmu(command.height, "addTextBox", "height");
15889
- if (typeof command.text !== "string") {
17186
+ if (command.text !== void 0 && typeof command.text !== "string") {
15890
17187
  throw new Error("addTextBox: text must be a string");
15891
17188
  }
15892
17189
  if (command.name !== void 0 && command.name.trim() === "") {
@@ -15917,6 +17214,22 @@ function clearTextRunPropertiesCommand(document, command) {
15917
17214
  }
15918
17215
  return clearTextRunProperties(document, command.handle, command.properties);
15919
17216
  }
17217
+ function setParagraphPropertiesCommand(document, command) {
17218
+ requireNonEmptyParagraphPropertySet(command.properties, "setParagraphProperties");
17219
+ validateParagraphPropertySet(command.properties, "setParagraphProperties");
17220
+ return setParagraphProperties(document, command.handle, command.properties);
17221
+ }
17222
+ function clearParagraphPropertiesCommand(document, command) {
17223
+ if (command.properties.length === 0) {
17224
+ throw new Error("clearParagraphProperties: properties must contain at least one property name");
17225
+ }
17226
+ for (const property of command.properties) {
17227
+ if (!EDITABLE_PARAGRAPH_PROPERTY_SET2.has(property)) {
17228
+ throw new Error(`clearParagraphProperties: unsupported paragraph property '${property}'`);
17229
+ }
17230
+ }
17231
+ return clearParagraphProperties(document, command.handle, command.properties);
17232
+ }
15920
17233
  function moveShape(document, command) {
15921
17234
  requireFiniteEmu(command.offsetX, "moveShape", "offsetX");
15922
17235
  requireFiniteEmu(command.offsetY, "moveShape", "offsetY");
@@ -15952,6 +17265,14 @@ function setShapeTransform(document, command) {
15952
17265
  height: command.height
15953
17266
  });
15954
17267
  }
17268
+ function setShapeFillCommand(document, command) {
17269
+ validateShapeFill(command.fill, "setShapeFill");
17270
+ return setShapeFill(document, command.handle, command.fill);
17271
+ }
17272
+ function setShapeOutlineCommand(document, command) {
17273
+ validateShapeOutline(command.outline, "setShapeOutline");
17274
+ return setShapeOutline(document, command.handle, command.outline);
17275
+ }
15955
17276
  var EDITABLE_TEXT_RUN_PROPERTIES2 = [
15956
17277
  "bold",
15957
17278
  "italic",
@@ -15961,6 +17282,24 @@ var EDITABLE_TEXT_RUN_PROPERTIES2 = [
15961
17282
  "typeface"
15962
17283
  ];
15963
17284
  var EDITABLE_TEXT_RUN_PROPERTY_SET2 = new Set(EDITABLE_TEXT_RUN_PROPERTIES2);
17285
+ var EDITABLE_PARAGRAPH_PROPERTIES22 = [
17286
+ "align",
17287
+ "level",
17288
+ "bullet"
17289
+ ];
17290
+ var EDITABLE_PARAGRAPH_PROPERTY_SET2 = new Set(EDITABLE_PARAGRAPH_PROPERTIES22);
17291
+ var PARAGRAPH_ALIGN_VALUES = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
17292
+ var AUTO_NUM_SCHEMES22 = /* @__PURE__ */ new Set([
17293
+ "arabicPeriod",
17294
+ "arabicParenR",
17295
+ "romanUcPeriod",
17296
+ "romanLcPeriod",
17297
+ "alphaUcPeriod",
17298
+ "alphaLcPeriod",
17299
+ "alphaLcParenR",
17300
+ "alphaUcParenR",
17301
+ "arabicPlain"
17302
+ ]);
15964
17303
  function requireNonEmptyPropertySet(properties, commandName) {
15965
17304
  if (Object.values(properties).every((value) => value === void 0)) {
15966
17305
  throw new Error(`${commandName}: properties must contain at least one defined property`);
@@ -15990,11 +17329,72 @@ function validateTextRunPropertySet(properties, commandName) {
15990
17329
  }
15991
17330
  }
15992
17331
  }
17332
+ function requireNonEmptyParagraphPropertySet(properties, commandName) {
17333
+ if (Object.values(properties).every((value) => value === void 0)) {
17334
+ throw new Error(`${commandName}: properties must contain at least one defined property`);
17335
+ }
17336
+ }
17337
+ function validateParagraphPropertySet(properties, commandName) {
17338
+ for (const property of Object.keys(properties)) {
17339
+ if (!EDITABLE_PARAGRAPH_PROPERTY_SET2.has(property)) {
17340
+ throw new Error(`${commandName}: unsupported paragraph property '${property}'`);
17341
+ }
17342
+ }
17343
+ if (properties.align !== void 0 && !PARAGRAPH_ALIGN_VALUES.has(properties.align)) {
17344
+ throw new Error(`${commandName}: align must be left, center, right, or justify`);
17345
+ }
17346
+ if (properties.level !== void 0 && (!Number.isInteger(properties.level) || properties.level < 0 || properties.level > 8)) {
17347
+ throw new Error(`${commandName}: level must be an integer from 0 to 8`);
17348
+ }
17349
+ if (properties.bullet !== void 0) {
17350
+ validateParagraphBullet2(properties.bullet, commandName);
17351
+ }
17352
+ }
17353
+ function validateParagraphBullet2(bullet, commandName) {
17354
+ if (bullet.type === "none") return;
17355
+ if (bullet.type === "char") {
17356
+ if (bullet.char.length === 0) {
17357
+ throw new Error(`${commandName}: bullet.char must be a non-empty string`);
17358
+ }
17359
+ return;
17360
+ }
17361
+ if (bullet.type === "autoNum") {
17362
+ if (!AUTO_NUM_SCHEMES22.has(bullet.scheme)) {
17363
+ throw new Error(`${commandName}: unsupported bullet auto-numbering scheme`);
17364
+ }
17365
+ if (!Number.isInteger(bullet.startAt) || bullet.startAt < 1) {
17366
+ throw new Error(`${commandName}: bullet.startAt must be a positive integer`);
17367
+ }
17368
+ return;
17369
+ }
17370
+ throw new Error(`${commandName}: unsupported bullet type`);
17371
+ }
15993
17372
  function requireBooleanOrUndefined2(value, commandName, fieldName) {
15994
17373
  if (value !== void 0 && typeof value !== "boolean") {
15995
17374
  throw new Error(`${commandName}: ${fieldName} must be a boolean value`);
15996
17375
  }
15997
17376
  }
17377
+ function validateShapeOutline(outline, commandName) {
17378
+ if (outline.width === void 0 && outline.fill === void 0) {
17379
+ throw new Error(`${commandName}: outline must set width or fill`);
17380
+ }
17381
+ if (outline.width !== void 0) {
17382
+ requirePositiveFiniteEmu(outline.width, commandName, "width");
17383
+ }
17384
+ if (outline.fill !== void 0) validateShapeFill(outline.fill, commandName);
17385
+ }
17386
+ function validateShapeFill(fill, commandName) {
17387
+ if (fill.kind === "none") return;
17388
+ if (fill.kind !== "solid") {
17389
+ throw new Error(`${commandName}: only solid and none fills are supported`);
17390
+ }
17391
+ if (fill.color.kind !== "srgb") {
17392
+ throw new Error(`${commandName}: only srgb solid fill colors are supported`);
17393
+ }
17394
+ if (!/^[0-9A-Fa-f]{6}$/.test(fill.color.hex)) {
17395
+ throw new Error(`${commandName}: color.hex must be a 6-digit hex value`);
17396
+ }
17397
+ }
15998
17398
  function requireEditableShapeTransform(document, handle, commandName) {
15999
17399
  const shape = findShapeNodeBySourceHandle(document, handle);
16000
17400
  if (shape === void 0) {
@@ -16023,8 +17423,12 @@ function normalizeEditorEdits(document) {
16023
17423
  if (edits === void 0) return document;
16024
17424
  const seenTextRuns = /* @__PURE__ */ new Set();
16025
17425
  const seenTextRunProperties = /* @__PURE__ */ new Map();
17426
+ const seenParagraphProperties = /* @__PURE__ */ new Map();
16026
17427
  const seenParagraphs = /* @__PURE__ */ new Set();
16027
17428
  const seenShapeTransforms = /* @__PURE__ */ new Set();
17429
+ const seenShapeFills = /* @__PURE__ */ new Set();
17430
+ const seenShapeOutlineProperties = /* @__PURE__ */ new Map();
17431
+ const normalizedShapeOutlineEdits = /* @__PURE__ */ new Map();
16028
17432
  const normalizedReversed = [];
16029
17433
  let changed = false;
16030
17434
  for (let index = edits.length - 1; index >= 0; index -= 1) {
@@ -16057,6 +17461,16 @@ function normalizeEditorEdits(document) {
16057
17461
  normalizedReversed.push(normalized);
16058
17462
  continue;
16059
17463
  }
17464
+ if (edit.kind === "updateParagraphProperties") {
17465
+ const normalized = normalizeParagraphPropertiesEdit(edit, seenParagraphProperties);
17466
+ if (normalized === void 0) {
17467
+ changed = true;
17468
+ continue;
17469
+ }
17470
+ if (!editorEditsEqual(normalized, edit)) changed = true;
17471
+ normalizedReversed.push(normalized);
17472
+ continue;
17473
+ }
16060
17474
  if (edit.kind === "replaceParagraphPlainText") {
16061
17475
  const key = editHandleNodeKey2(edit);
16062
17476
  if (seenParagraphs.has(key)) {
@@ -16073,6 +17487,32 @@ function normalizeEditorEdits(document) {
16073
17487
  }
16074
17488
  seenShapeTransforms.add(key);
16075
17489
  }
17490
+ if (edit.kind === "updateShapeFill") {
17491
+ const key = editHandleNodeKey2(edit);
17492
+ if (seenShapeFills.has(key)) {
17493
+ changed = true;
17494
+ continue;
17495
+ }
17496
+ seenShapeFills.add(key);
17497
+ }
17498
+ if (edit.kind === "updateShapeOutline") {
17499
+ const normalized = normalizeShapeOutlineEdit(
17500
+ edit,
17501
+ seenShapeOutlineProperties,
17502
+ normalizedShapeOutlineEdits
17503
+ );
17504
+ if (normalized === void 0) {
17505
+ changed = true;
17506
+ continue;
17507
+ }
17508
+ if (normalized.merged) {
17509
+ changed = true;
17510
+ continue;
17511
+ }
17512
+ if (!editorEditsEqual(normalized.edit, edit)) changed = true;
17513
+ normalizedReversed.push(normalized.edit);
17514
+ continue;
17515
+ }
16076
17516
  normalizedReversed.push(edit);
16077
17517
  }
16078
17518
  if (!changed && normalizedReversed.length === edits.length) return document;
@@ -16149,6 +17589,67 @@ function normalizeTextRunPropertiesEdit(edit, seenTextRunProperties) {
16149
17589
  };
16150
17590
  return normalized;
16151
17591
  }
17592
+ function normalizeParagraphPropertiesEdit(edit, seenParagraphProperties) {
17593
+ const key = editHandleNodeKey2(edit);
17594
+ let seenProperties = seenParagraphProperties.get(key);
17595
+ if (seenProperties === void 0) {
17596
+ seenProperties = /* @__PURE__ */ new Set();
17597
+ seenParagraphProperties.set(key, seenProperties);
17598
+ }
17599
+ const set = {};
17600
+ if (edit.set?.align !== void 0 && !seenProperties.has("align")) {
17601
+ seenProperties.add("align");
17602
+ set.align = edit.set.align;
17603
+ }
17604
+ if (edit.set?.level !== void 0 && !seenProperties.has("level")) {
17605
+ seenProperties.add("level");
17606
+ set.level = edit.set.level;
17607
+ }
17608
+ if (edit.set?.bullet !== void 0 && !seenProperties.has("bullet")) {
17609
+ seenProperties.add("bullet");
17610
+ set.bullet = edit.set.bullet;
17611
+ }
17612
+ const clear = (edit.clear ?? []).filter((property) => !seenProperties.has(property));
17613
+ for (const property of clear) seenProperties.add(property);
17614
+ if (clear.length === 0 && Object.keys(set).length === 0) return void 0;
17615
+ const normalized = {
17616
+ kind: "updateParagraphProperties",
17617
+ handle: edit.handle,
17618
+ ...Object.keys(set).length > 0 ? { set } : {},
17619
+ ...clear.length > 0 ? { clear } : {}
17620
+ };
17621
+ return normalized;
17622
+ }
17623
+ function normalizeShapeOutlineEdit(edit, seenShapeOutlineProperties, normalizedShapeOutlineEdits) {
17624
+ const key = editHandleNodeKey2(edit);
17625
+ let seenProperties = seenShapeOutlineProperties.get(key);
17626
+ if (seenProperties === void 0) {
17627
+ seenProperties = /* @__PURE__ */ new Set();
17628
+ seenShapeOutlineProperties.set(key, seenProperties);
17629
+ }
17630
+ const outline = {};
17631
+ if (edit.outline.width !== void 0 && !seenProperties.has("width")) {
17632
+ seenProperties.add("width");
17633
+ outline.width = edit.outline.width;
17634
+ }
17635
+ if (edit.outline.fill !== void 0 && !seenProperties.has("fill")) {
17636
+ seenProperties.add("fill");
17637
+ outline.fill = edit.outline.fill;
17638
+ }
17639
+ if (Object.keys(outline).length === 0) return void 0;
17640
+ const existing = normalizedShapeOutlineEdits.get(key);
17641
+ if (existing !== void 0) {
17642
+ existing.outline = { ...outline, ...existing.outline };
17643
+ return { merged: true };
17644
+ }
17645
+ const normalized = {
17646
+ kind: "updateShapeOutline",
17647
+ handle: edit.handle,
17648
+ outline
17649
+ };
17650
+ normalizedShapeOutlineEdits.set(key, normalized);
17651
+ return { edit: normalized, merged: false };
17652
+ }
16152
17653
 
16153
17654
  // src/browser-editor.ts
16154
17655
  var EMU_PER_INCH2 = 914400;
@@ -16161,6 +17662,12 @@ var DEFAULT_TEXT_BOX_BOUNDS_PX = {
16161
17662
  height: 72
16162
17663
  };
16163
17664
  var DEFAULT_TEXT_BOX_TEXT = "New text box";
17665
+ var DEFAULT_CONNECTOR_BOUNDS_PX = {
17666
+ x: 144,
17667
+ y: 144,
17668
+ width: 288,
17669
+ height: 96
17670
+ };
16164
17671
  var IMAGE_REL_TYPE3 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
16165
17672
  var IMAGE_ACCEPT_BY_CONTENT_TYPE = {
16166
17673
  "image/png": "image/png,.png",
@@ -16258,6 +17765,32 @@ var BrowserPptxEditorSession = class _BrowserPptxEditorSession {
16258
17765
  await this.renderCurrentSlides();
16259
17766
  return this.response(result.warnings);
16260
17767
  }
17768
+ async addConnector(slideNumber = 1, options = {}) {
17769
+ const slide = this.#session.document.slides[slideNumber - 1];
17770
+ if (slide?.handle === void 0) {
17771
+ throw new Error("addConnector: slide handle was not found in PptxSourceModel source");
17772
+ }
17773
+ const existingShapeKeys = new Set(slide.shapes.map(shapeSourceKey));
17774
+ const result = this.#session.apply({
17775
+ kind: "addConnector",
17776
+ slideHandle: slide.handle,
17777
+ preset: "straightConnector1",
17778
+ offsetX: pxToEmu(options.x ?? DEFAULT_CONNECTOR_BOUNDS_PX.x),
17779
+ offsetY: pxToEmu(options.y ?? DEFAULT_CONNECTOR_BOUNDS_PX.y),
17780
+ width: pxToEmu(options.width ?? DEFAULT_CONNECTOR_BOUNDS_PX.width),
17781
+ height: pxToEmu(options.height ?? DEFAULT_CONNECTOR_BOUNDS_PX.height),
17782
+ outline: {
17783
+ tailEnd: { type: "triangle", width: "med", length: "med" }
17784
+ },
17785
+ ...options.name !== void 0 ? { name: options.name } : {}
17786
+ });
17787
+ if (!result.ok) {
17788
+ throw new Error(result.message);
17789
+ }
17790
+ this.#selectNewShape(slideNumber, existingShapeKeys);
17791
+ await this.renderCurrentSlides();
17792
+ return this.response(result.warnings);
17793
+ }
16261
17794
  async deleteShape(handle) {
16262
17795
  const result = this.#session.apply({ kind: "deleteShape", handle });
16263
17796
  if (!result.ok) {
@@ -16370,10 +17903,10 @@ function isEditableTransformShape(shape) {
16370
17903
  return !shape.rawSidecars?.some((sidecar) => sidecar.node.name === "mc:AlternateContent");
16371
17904
  }
16372
17905
  function isDeletableShape(shape, slideShapes) {
16373
- if (shape.kind !== "shape" || shape.handle?.nodeId === void 0) {
17906
+ if (shape.kind !== "shape" && shape.kind !== "connector" || shape.handle?.nodeId === void 0) {
16374
17907
  return false;
16375
17908
  }
16376
- if (isShapeReferencedByConnector(shape, slideShapes)) {
17909
+ if (shape.kind === "shape" && isShapeReferencedByConnector(shape, slideShapes)) {
16377
17910
  return false;
16378
17911
  }
16379
17912
  return !shape.rawSidecars?.some((sidecar) => sidecar.node.name === "mc:AlternateContent");