pptx-glimpse 3.1.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.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");
@@ -7974,16 +8449,7 @@ function buildConnectorXml(params) {
7974
8449
  "@_id": params.shapeId,
7975
8450
  "@_name": params.name
7976
8451
  },
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
- },
8452
+ "p:cNvCxnSpPr": createConnectorConnectionXml(params),
7987
8453
  "p:nvPr": {}
7988
8454
  },
7989
8455
  "p:spPr": {
@@ -8006,6 +8472,22 @@ function buildConnectorXml(params) {
8006
8472
  }
8007
8473
  });
8008
8474
  }
8475
+ function createConnectorConnectionXml(params) {
8476
+ return {
8477
+ ...params.startShapeId !== void 0 && params.startConnectionSiteIndex !== void 0 ? {
8478
+ "a:stCxn": {
8479
+ "@_id": params.startShapeId,
8480
+ "@_idx": String(params.startConnectionSiteIndex)
8481
+ }
8482
+ } : {},
8483
+ ...params.endShapeId !== void 0 && params.endConnectionSiteIndex !== void 0 ? {
8484
+ "a:endCxn": {
8485
+ "@_id": params.endShapeId,
8486
+ "@_idx": String(params.endConnectionSiteIndex)
8487
+ }
8488
+ } : {}
8489
+ };
8490
+ }
8009
8491
  function createConnectorLineXml(params) {
8010
8492
  return {
8011
8493
  "a:solidFill": {
@@ -8045,8 +8527,8 @@ var CONNECTOR_PRESETS = /* @__PURE__ */ new Set([
8045
8527
  "bentConnector3",
8046
8528
  "curvedConnector3"
8047
8529
  ]);
8048
- var ARROW_TYPES = /* @__PURE__ */ new Set(["triangle", "stealth", "diamond", "oval", "arrow"]);
8049
- var ARROW_SIZES = /* @__PURE__ */ new Set(["sm", "med", "lg"]);
8530
+ var ARROW_TYPES2 = /* @__PURE__ */ new Set(["triangle", "stealth", "diamond", "oval", "arrow"]);
8531
+ var ARROW_SIZES2 = /* @__PURE__ */ new Set(["sm", "med", "lg"]);
8050
8532
  function findShapeNodeBySourceHandle(source, handle) {
8051
8533
  for (const slide of source.slides) {
8052
8534
  const shape = findShapeNodeInTree(slide.shapes, handle);
@@ -8110,6 +8592,92 @@ function updateShapeTransform(source, handle, transform) {
8110
8592
  ]
8111
8593
  };
8112
8594
  }
8595
+ function setShapeFill(source, handle, fill) {
8596
+ assertEditableShapeFill(fill, "setShapeFill");
8597
+ if (handle.nodeId === void 0) {
8598
+ throw new Error("setShapeFill: shape fill edit requires a node id");
8599
+ }
8600
+ let matched = false;
8601
+ let changed = false;
8602
+ const slides = source.slides.map((slide) => {
8603
+ let slideChanged = false;
8604
+ const shapes = slide.shapes.map((shape) => {
8605
+ if (!sourceHandlesEqual(shape.handle, handle)) return shape;
8606
+ matched = true;
8607
+ if (shape.kind !== "shape") {
8608
+ throw new Error("setShapeFill: only top-level sp shapes support fill edits");
8609
+ }
8610
+ if (hasAlternateContentSidecar(shape)) {
8611
+ throw new Error("setShapeFill: shapes inside AlternateContent are not supported");
8612
+ }
8613
+ const nextFill = toSourceFill(fill);
8614
+ if (sourceFillEqual(shape.fill, nextFill)) return shape;
8615
+ changed = true;
8616
+ slideChanged = true;
8617
+ return {
8618
+ ...shape,
8619
+ fill: nextFill
8620
+ };
8621
+ });
8622
+ return slideChanged ? { ...slide, shapes } : slide;
8623
+ });
8624
+ if (!matched) {
8625
+ if (source.slides.some((slide) => hasNestedShapeNodeWithHandle(slide.shapes, handle))) {
8626
+ throw new Error("setShapeFill: nested group shape editing is not supported");
8627
+ }
8628
+ throw new Error("setShapeFill: shape handle was not found in PptxSourceModel source");
8629
+ }
8630
+ if (!changed) return source;
8631
+ return {
8632
+ ...source,
8633
+ slides,
8634
+ edits: appendShapeFillEdit(source.edits ?? [], handle, fill)
8635
+ };
8636
+ }
8637
+ function setShapeOutline(source, handle, outline) {
8638
+ assertEditableShapeOutline(outline, "setShapeOutline");
8639
+ if (handle.nodeId === void 0) {
8640
+ throw new Error("setShapeOutline: shape outline edit requires a node id");
8641
+ }
8642
+ let matched = false;
8643
+ let changed = false;
8644
+ const slides = source.slides.map((slide) => {
8645
+ let slideChanged = false;
8646
+ const shapes = slide.shapes.map((shape) => {
8647
+ if (!sourceHandlesEqual(shape.handle, handle)) return shape;
8648
+ matched = true;
8649
+ if (shape.kind !== "shape" && shape.kind !== "connector") {
8650
+ throw new Error(
8651
+ "setShapeOutline: only top-level sp and cxnSp shapes support outline edits"
8652
+ );
8653
+ }
8654
+ if (hasAlternateContentSidecar(shape)) {
8655
+ throw new Error("setShapeOutline: shapes inside AlternateContent are not supported");
8656
+ }
8657
+ const nextOutline = patchSourceOutline(shape.outline, outline);
8658
+ if (sourceOutlineEqual(shape.outline, nextOutline)) return shape;
8659
+ changed = true;
8660
+ slideChanged = true;
8661
+ return {
8662
+ ...shape,
8663
+ outline: nextOutline
8664
+ };
8665
+ });
8666
+ return slideChanged ? { ...slide, shapes } : slide;
8667
+ });
8668
+ if (!matched) {
8669
+ if (source.slides.some((slide) => hasNestedShapeNodeWithHandle(slide.shapes, handle))) {
8670
+ throw new Error("setShapeOutline: nested group shape editing is not supported");
8671
+ }
8672
+ throw new Error("setShapeOutline: shape handle was not found in PptxSourceModel source");
8673
+ }
8674
+ if (!changed) return source;
8675
+ return {
8676
+ ...source,
8677
+ slides,
8678
+ edits: appendShapeOutlineEdit(source.edits ?? [], handle, outline)
8679
+ };
8680
+ }
8113
8681
  function addTextBox(source, slideHandle, input) {
8114
8682
  assertTextBoxInput(input);
8115
8683
  const slideIndex = source.slides.findIndex(
@@ -8162,14 +8730,14 @@ function addConnector(source, slideHandle, input) {
8162
8730
  throw new Error("addConnector: slide handle was not found in PptxSourceModel source");
8163
8731
  }
8164
8732
  const slide = source.slides[slideIndex];
8165
- const startShape = requireConnectorTargetShape(slide, input.start.shapeHandle, "start");
8166
- const endShape = requireConnectorTargetShape(slide, input.end.shapeHandle, "end");
8733
+ const startShape = input.start !== void 0 ? requireConnectorTargetShape(slide, input.start.shapeHandle, "start") : void 0;
8734
+ const endShape = input.end !== void 0 ? requireConnectorTargetShape(slide, input.end.shapeHandle, "end") : void 0;
8167
8735
  const shapeId = nextShapeId(slide.shapes, source.edits ?? [], slide.partPath);
8168
8736
  const shapeIdValue = String(shapeId);
8169
8737
  const name = input.name?.trim() || `Connector ${shapeIdValue}`;
8170
8738
  const orderingSlot = nextOrderingSlot(slide.shapes);
8171
- const startShapeId = String(startShape.nodeId);
8172
- const endShapeId = String(endShape.nodeId);
8739
+ const startShapeId = startShape !== void 0 ? String(startShape.nodeId) : void 0;
8740
+ const endShapeId = endShape !== void 0 ? String(endShape.nodeId) : void 0;
8173
8741
  const xml = buildConnectorXml({
8174
8742
  shapeId: shapeIdValue,
8175
8743
  name,
@@ -8178,10 +8746,14 @@ function addConnector(source, slideHandle, input) {
8178
8746
  offsetY: input.offsetY,
8179
8747
  width: input.width,
8180
8748
  height: input.height,
8181
- startShapeId,
8182
- startConnectionSiteIndex: input.start.connectionSiteIndex,
8183
- endShapeId,
8184
- endConnectionSiteIndex: input.end.connectionSiteIndex,
8749
+ ...startShapeId !== void 0 && input.start !== void 0 ? {
8750
+ startShapeId,
8751
+ startConnectionSiteIndex: input.start.connectionSiteIndex
8752
+ } : {},
8753
+ ...endShapeId !== void 0 && input.end !== void 0 ? {
8754
+ endShapeId,
8755
+ endConnectionSiteIndex: input.end.connectionSiteIndex
8756
+ } : {},
8185
8757
  ...input.outline !== void 0 ? { outline: input.outline } : {}
8186
8758
  });
8187
8759
  const connector = parseShapeNodeXml(xml, slide.partPath, orderingSlot);
@@ -8200,8 +8772,8 @@ function addConnector(source, slideHandle, input) {
8200
8772
  kind: "addConnector",
8201
8773
  slidePartPath: slide.partPath,
8202
8774
  shapeId: shapeIdValue,
8203
- startShapeId,
8204
- endShapeId,
8775
+ ...startShapeId !== void 0 ? { startShapeId } : {},
8776
+ ...endShapeId !== void 0 ? { endShapeId } : {},
8205
8777
  xml
8206
8778
  }
8207
8779
  ]
@@ -8218,8 +8790,8 @@ function deleteShape(source, handle) {
8218
8790
  const nextShapes = slide.shapes.filter((shape) => {
8219
8791
  if (!sourceHandlesEqual(shape.handle, handle)) return true;
8220
8792
  found2 = shape;
8221
- if (shape.kind !== "shape") {
8222
- throw new Error("deleteShape: only top-level sp shapes can be deleted");
8793
+ if (shape.kind !== "shape" && shape.kind !== "connector") {
8794
+ throw new Error("deleteShape: only top-level sp or cxnSp shapes can be deleted");
8223
8795
  }
8224
8796
  if (hasAlternateContentSidecar(shape)) {
8225
8797
  throw new Error("deleteShape: shapes inside AlternateContent are not supported");
@@ -8236,31 +8808,124 @@ function deleteShape(source, handle) {
8236
8808
  }
8237
8809
  throw new Error("deleteShape: shape handle was not found in PptxSourceModel source");
8238
8810
  }
8239
- const referencingConnector = findConnectorReferencingShape(source, handle);
8240
- if (referencingConnector !== void 0) {
8241
- throw new Error(
8242
- `deleteShape: shape is referenced by connector '${referencingConnector.name ?? referencingConnector.nodeId ?? "unknown"}'`
8243
- );
8811
+ const referencingConnector = findConnectorReferencingShape(source, handle);
8812
+ if (referencingConnector !== void 0) {
8813
+ throw new Error(
8814
+ `deleteShape: shape is referenced by connector '${referencingConnector.name ?? referencingConnector.nodeId ?? "unknown"}'`
8815
+ );
8816
+ }
8817
+ const retainedEdits = (source.edits ?? []).filter((edit) => !editTargetsShape(edit, handle));
8818
+ const deletedInsertedShape = (source.edits ?? []).some((edit) => {
8819
+ const inserted = editInsertedShape(edit);
8820
+ return inserted !== void 0 && inserted.slidePartPath === handle.partPath && inserted.shapeId === String(handle.nodeId);
8821
+ });
8822
+ return {
8823
+ ...source,
8824
+ slides,
8825
+ edits: deletedInsertedShape ? retainedEdits : [
8826
+ ...retainedEdits,
8827
+ {
8828
+ kind: "deleteShape",
8829
+ handle
8830
+ }
8831
+ ]
8832
+ };
8833
+ }
8834
+ function shapeTransformPositionAndSizeEqual(current, next) {
8835
+ return current?.offsetX === next.offsetX && current.offsetY === next.offsetY && current.width === next.width && current.height === next.height;
8836
+ }
8837
+ function patchSourceOutline(current, patch) {
8838
+ return {
8839
+ ...current ?? {},
8840
+ ...patch.width !== void 0 ? { width: patch.width } : {},
8841
+ ...patch.fill !== void 0 ? { fill: toSourceFill(patch.fill) } : {}
8842
+ };
8843
+ }
8844
+ function appendShapeFillEdit(edits, handle, fill) {
8845
+ const retainedEdits = edits.filter(
8846
+ (edit) => edit.kind !== "updateShapeFill" || !sourceHandlesEqual(edit.handle, handle)
8847
+ );
8848
+ return [...retainedEdits, { kind: "updateShapeFill", handle, fill }];
8849
+ }
8850
+ function appendShapeOutlineEdit(edits, handle, outline) {
8851
+ let outlineEdit = {
8852
+ kind: "updateShapeOutline",
8853
+ handle,
8854
+ outline
8855
+ };
8856
+ const retainedEdits = [];
8857
+ for (const edit of edits) {
8858
+ if (edit.kind !== "updateShapeOutline" || !sourceHandlesEqual(edit.handle, handle)) {
8859
+ retainedEdits.push(edit);
8860
+ continue;
8861
+ }
8862
+ outlineEdit = {
8863
+ ...outlineEdit,
8864
+ outline: mergeEditableShapeOutline(edit.outline, outlineEdit.outline)
8865
+ };
8866
+ }
8867
+ return [...retainedEdits, outlineEdit];
8868
+ }
8869
+ function mergeEditableShapeOutline(base, patch) {
8870
+ return {
8871
+ ...base.width !== void 0 ? { width: base.width } : {},
8872
+ ...base.fill !== void 0 ? { fill: base.fill } : {},
8873
+ ...patch.width !== void 0 ? { width: patch.width } : {},
8874
+ ...patch.fill !== void 0 ? { fill: patch.fill } : {}
8875
+ };
8876
+ }
8877
+ function toSourceFill(fill) {
8878
+ if (fill.kind === "none") return { kind: "none" };
8879
+ return {
8880
+ kind: "solid",
8881
+ color: { kind: "srgb", hex: fill.color.hex }
8882
+ };
8883
+ }
8884
+ function assertEditableShapeFill(fill, operationName) {
8885
+ if (fill.kind === "none") return;
8886
+ if (fill.kind !== "solid") {
8887
+ throw new Error(`${operationName}: only solid and none fills are supported`);
8888
+ }
8889
+ if (fill.color.kind !== "srgb") {
8890
+ throw new Error(`${operationName}: only srgb solid fill colors are supported`);
8891
+ }
8892
+ if (!/^[0-9A-Fa-f]{6}$/.test(fill.color.hex)) {
8893
+ throw new Error(`${operationName}: srgb fill color must be a 6-digit hex value`);
8894
+ }
8895
+ }
8896
+ function assertEditableShapeOutline(outline, operationName) {
8897
+ if (outline.width === void 0 && outline.fill === void 0) {
8898
+ throw new Error(`${operationName}: outline must set width or fill`);
8899
+ }
8900
+ if (outline.width !== void 0) {
8901
+ assertPositiveFiniteEmu(outline.width, operationName, "width");
8902
+ }
8903
+ if (outline.fill !== void 0) assertEditableShapeFill(outline.fill, operationName);
8904
+ }
8905
+ function sourceFillEqual(left, right) {
8906
+ return stableValueEqual(left ?? {}, right ?? {});
8907
+ }
8908
+ function sourceOutlineEqual(left, right) {
8909
+ return stableValueEqual(left ?? {}, right ?? {});
8910
+ }
8911
+ function stableValueEqual(left, right) {
8912
+ if (Object.is(left, right)) return true;
8913
+ if (Array.isArray(left) || Array.isArray(right)) {
8914
+ if (!Array.isArray(left) || !Array.isArray(right)) return false;
8915
+ if (left.length !== right.length) return false;
8916
+ return left.every((value, index) => stableValueEqual(value, right[index]));
8917
+ }
8918
+ if (isPlainRecord(left) || isPlainRecord(right)) {
8919
+ if (!isPlainRecord(left) || !isPlainRecord(right)) return false;
8920
+ const leftKeys = Object.keys(left).sort();
8921
+ const rightKeys = Object.keys(right).sort();
8922
+ if (!stableValueEqual(leftKeys, rightKeys)) return false;
8923
+ return leftKeys.every((key) => stableValueEqual(left[key], right[key]));
8244
8924
  }
8245
- const retainedEdits = (source.edits ?? []).filter((edit) => !editTargetsShape(edit, handle));
8246
- const deletedInsertedShape = (source.edits ?? []).some((edit) => {
8247
- const inserted = editInsertedShape(edit);
8248
- return inserted !== void 0 && inserted.slidePartPath === handle.partPath && inserted.shapeId === String(handle.nodeId);
8249
- });
8250
- return {
8251
- ...source,
8252
- slides,
8253
- edits: deletedInsertedShape ? retainedEdits : [
8254
- ...retainedEdits,
8255
- {
8256
- kind: "deleteShape",
8257
- handle
8258
- }
8259
- ]
8260
- };
8925
+ return false;
8261
8926
  }
8262
- function shapeTransformPositionAndSizeEqual(current, next) {
8263
- return current?.offsetX === next.offsetX && current.offsetY === next.offsetY && current.width === next.width && current.height === next.height;
8927
+ function isPlainRecord(value) {
8928
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8264
8929
  }
8265
8930
  function hasEditableTransform(shape) {
8266
8931
  return shape.kind !== "raw" && shape.transform !== void 0;
@@ -8287,8 +8952,9 @@ function assertConnectorInput(input) {
8287
8952
  "addConnector: preset must be straightConnector1, bentConnector3, or curvedConnector3"
8288
8953
  );
8289
8954
  }
8290
- assertConnectionSiteIndex(input.start.connectionSiteIndex, "start");
8291
- assertConnectionSiteIndex(input.end.connectionSiteIndex, "end");
8955
+ if (input.start !== void 0)
8956
+ assertConnectionSiteIndex(input.start.connectionSiteIndex, "start");
8957
+ if (input.end !== void 0) assertConnectionSiteIndex(input.end.connectionSiteIndex, "end");
8292
8958
  if (input.name !== void 0 && input.name.trim() === "") {
8293
8959
  throw new Error("addConnector: name must be a non-empty string when provided");
8294
8960
  }
@@ -8304,13 +8970,13 @@ function assertConnectionSiteIndex(value, fieldName) {
8304
8970
  }
8305
8971
  function assertArrowEndpoint(endpoint, fieldName) {
8306
8972
  if (endpoint === void 0) return;
8307
- if (!ARROW_TYPES.has(endpoint.type)) {
8973
+ if (!ARROW_TYPES2.has(endpoint.type)) {
8308
8974
  throw new Error(`addConnector: outline.${fieldName}.type is not supported`);
8309
8975
  }
8310
- if (!ARROW_SIZES.has(endpoint.width)) {
8976
+ if (!ARROW_SIZES2.has(endpoint.width)) {
8311
8977
  throw new Error(`addConnector: outline.${fieldName}.width is not supported`);
8312
8978
  }
8313
- if (!ARROW_SIZES.has(endpoint.length)) {
8979
+ if (!ARROW_SIZES2.has(endpoint.length)) {
8314
8980
  throw new Error(`addConnector: outline.${fieldName}.length is not supported`);
8315
8981
  }
8316
8982
  }
@@ -8705,848 +9371,621 @@ function duplicateSlide(source, slideHandle) {
8705
9371
  ]
8706
9372
  };
8707
9373
  }
8708
- function deleteSlide(source, slideHandle) {
8709
- if (source.slides.length <= 1) {
8710
- throw new Error("deleteSlide: cannot delete the last slide");
8711
- }
9374
+ function moveSlide(source, slideHandle, input) {
9375
+ assertValidSlideIndex(input.toIndex, source.slides.length, "moveSlide");
8712
9376
  const slideIndex = source.slides.findIndex(
8713
9377
  (slide2) => sourceHandlesEqual(slide2.handle, slideHandle)
8714
9378
  );
8715
9379
  if (slideIndex === -1) {
8716
- throw new Error("deleteSlide: slide handle was not found in PptxSourceModel source");
9380
+ throw new Error("moveSlide: slide handle was not found in PptxSourceModel source");
8717
9381
  }
9382
+ if (slideIndex === input.toIndex) return source;
8718
9383
  const slide = source.slides[slideIndex];
8719
9384
  const presentationRels = requirePartRelationships(
8720
9385
  source,
8721
9386
  source.presentation.partPath,
8722
- "deleteSlide"
9387
+ "moveSlide"
8723
9388
  );
8724
9389
  const presentationRelationship = requireSlideRelationship(
8725
9390
  source,
8726
9391
  presentationRels,
8727
9392
  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
9393
+ "moveSlide"
8750
9394
  );
8751
9395
  return {
8752
9396
  ...source,
8753
9397
  presentation: {
8754
9398
  ...source.presentation,
8755
- slidePartPaths: source.presentation.slidePartPaths.filter(
8756
- (partPath) => partPath !== slide.partPath
8757
- )
9399
+ slidePartPaths: moveReadonly(source.presentation.slidePartPaths, slideIndex, input.toIndex)
8758
9400
  },
8759
- slides: source.slides.filter((candidate) => candidate.partPath !== slide.partPath),
8760
- packageGraph,
8761
- edits: deletedInsertedSlide ? retainedEdits : [
8762
- ...retainedEdits,
9401
+ slides: moveReadonly(source.slides, slideIndex, input.toIndex),
9402
+ edits: [
9403
+ ...source.edits ?? [],
8763
9404
  {
8764
- kind: "deleteSlide",
9405
+ kind: "moveSlide",
8765
9406
  slidePartPath: slide.partPath,
8766
- relationshipId: presentationRelationship.id
9407
+ relationshipId: presentationRelationship.id,
9408
+ toIndex: input.toIndex
8767
9409
  }
8768
9410
  ]
8769
9411
  };
8770
9412
  }
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
- );
9413
+ function deleteSlide(source, slideHandle) {
9414
+ if (source.slides.length <= 1) {
9415
+ throw new Error("deleteSlide: cannot delete the last slide");
8947
9416
  }
8948
- if (!result.changed) return source;
9417
+ const slideIndex = source.slides.findIndex(
9418
+ (slide2) => sourceHandlesEqual(slide2.handle, slideHandle)
9419
+ );
9420
+ if (slideIndex === -1) {
9421
+ throw new Error("deleteSlide: slide handle was not found in PptxSourceModel source");
9422
+ }
9423
+ const slide = source.slides[slideIndex];
9424
+ const presentationRels = requirePartRelationships(
9425
+ source,
9426
+ source.presentation.partPath,
9427
+ "deleteSlide"
9428
+ );
9429
+ const presentationRelationship = requireSlideRelationship(
9430
+ source,
9431
+ presentationRels,
9432
+ slide.partPath,
9433
+ "deleteSlide"
9434
+ );
9435
+ const slideRelationships = source.packageGraph.relationships.find(
9436
+ (relationships) => relationships.sourcePartPath === slide.partPath
9437
+ );
9438
+ const notesPartPaths = slideRelationships?.relationships.flatMap((relationship) => {
9439
+ if (relationship.type !== NOTES_SLIDE_REL_TYPE) return [];
9440
+ const target = resolveInternalRelationshipTarget(slide.partPath, relationship);
9441
+ return target === void 0 ? [] : [target];
9442
+ }) ?? [];
9443
+ const removedPartPaths = [slide.partPath, ...notesPartPaths];
9444
+ const removedPartPathSet = new Set(removedPartPaths);
9445
+ const retainedEdits = (source.edits ?? []).filter(
9446
+ (edit) => !editIsInvalidatedByDeletedParts(edit, removedPartPathSet)
9447
+ );
9448
+ const deletedInsertedSlide = (source.edits ?? []).some(
9449
+ (edit) => editInsertedSlidePartPath(edit) === slide.partPath
9450
+ );
9451
+ const packageGraph = removePartRelationship(
9452
+ removePackageParts(source.packageGraph, removedPartPaths),
9453
+ source.presentation.partPath,
9454
+ presentationRelationship.id
9455
+ );
8949
9456
  return {
8950
9457
  ...source,
8951
- slides: result.slides,
8952
- edits: [
8953
- ...source.edits ?? [],
9458
+ presentation: {
9459
+ ...source.presentation,
9460
+ slidePartPaths: source.presentation.slidePartPaths.filter(
9461
+ (partPath) => partPath !== slide.partPath
9462
+ )
9463
+ },
9464
+ slides: source.slides.filter((candidate) => candidate.partPath !== slide.partPath),
9465
+ packageGraph,
9466
+ edits: deletedInsertedSlide ? retainedEdits : [
9467
+ ...retainedEdits,
8954
9468
  {
8955
- kind: "updateTextRunProperties",
8956
- handle,
8957
- ...Object.keys(set).length > 0 ? { set } : {},
8958
- ...patch.clear.length > 0 ? { clear: patch.clear } : {}
9469
+ kind: "deleteSlide",
9470
+ slidePartPath: slide.partPath,
9471
+ relationshipId: presentationRelationship.id
8959
9472
  }
8960
9473
  ]
8961
9474
  };
8962
9475
  }
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 };
9476
+ function createNotesSlideCopy(source, sourceSlide, slideRelationships, newSlidePartPath) {
9477
+ const notesRelationship = slideRelationships?.relationships.find(
9478
+ (relationship) => relationship.type === NOTES_SLIDE_REL_TYPE
9479
+ );
9480
+ if (notesRelationship === void 0) return void 0;
9481
+ const notesPartPath = resolveInternalRelationshipTarget(sourceSlide.partPath, notesRelationship);
9482
+ if (notesPartPath === void 0) return void 0;
9483
+ const raw = requireRawBinaryPart(source, notesPartPath, "duplicateSlide");
9484
+ const contentType = source.packageGraph.parts.find((part) => part.partPath === notesPartPath)?.contentType ?? NOTES_SLIDE_CONTENT_TYPE;
9485
+ const newPartPath = nextNumberedPartPath(
9486
+ source.packageGraph,
9487
+ source.edits?.flatMap((edit) => editReservedPartPaths(edit)) ?? [],
9488
+ "ppt/notesSlides/notesSlide",
9489
+ ".xml"
9490
+ );
9491
+ const notesRelationships = source.packageGraph.relationships.find(
9492
+ (relationships) => relationships.sourcePartPath === notesPartPath
9493
+ );
9494
+ return {
9495
+ slideRelationshipId: notesRelationship.id,
9496
+ newPartPath,
9497
+ contentType,
9498
+ raw,
9499
+ ...notesRelationships === void 0 ? {} : {
9500
+ relationships: {
9501
+ sourcePartPath: newPartPath,
9502
+ relationships: notesRelationships.relationships.map(
9503
+ (relationship) => relationship.type === SLIDE_REL_TYPE && relationship.targetMode !== "External" ? { ...relationship, target: relativeTarget(newPartPath, newSlidePartPath) } : relationship
9504
+ )
9505
+ }
8985
9506
  }
8986
- return { paragraph: mapParagraph(paragraph), matched: true };
8987
- });
9507
+ };
8988
9508
  }
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
- });
9509
+ function withPartPath(slide, partPath) {
9017
9510
  return {
9018
- slides: changed ? slides : source.slides,
9019
- matched,
9020
- changed
9511
+ ...slide,
9512
+ partPath,
9513
+ handle: { partPath },
9514
+ shapes: slide.shapes.map((shape) => withShapePartPath(shape, partPath))
9021
9515
  };
9022
9516
  }
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]));
9517
+ function withShapePartPath(shape, partPath) {
9518
+ if (shape.kind === "raw") return shape;
9519
+ const handle = shape.handle === void 0 ? void 0 : { ...shape.handle, partPath };
9520
+ if (shape.kind === "group") {
9521
+ return {
9522
+ ...shape,
9523
+ ...handle !== void 0 ? { handle } : {},
9524
+ children: shape.children.map((child) => withShapePartPath(child, partPath))
9525
+ };
9082
9526
  }
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]));
9527
+ if (shape.kind !== "shape" || shape.textBody === void 0) {
9528
+ return { ...shape, ...handle !== void 0 ? { handle } : {} };
9089
9529
  }
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
9530
  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);
9531
+ ...shape,
9532
+ ...handle !== void 0 ? { handle } : {},
9533
+ textBody: {
9534
+ ...shape.textBody,
9535
+ paragraphs: shape.textBody.paragraphs.map((paragraph) => ({
9536
+ ...paragraph,
9537
+ ...paragraph.handle !== void 0 ? { handle: { ...paragraph.handle, partPath } } : {},
9538
+ runs: paragraph.runs.map((run) => ({
9539
+ ...run,
9540
+ ...run.handle !== void 0 ? { handle: { ...run.handle, partPath } } : {}
9541
+ }))
9542
+ }))
9543
+ }
9544
+ };
9112
9545
  }
9113
- function asOoxmlPercent(value) {
9114
- return unsafeBrandAssertion2(value);
9546
+ function assertValidSlideIndex(index, slideCount, operationName) {
9547
+ if (!Number.isInteger(index) || index < 0 || index >= slideCount) {
9548
+ throw new Error(`${operationName}: toIndex must be an integer slide index in range`);
9549
+ }
9115
9550
  }
9116
- function asOoxmlAngle(value) {
9117
- return unsafeBrandAssertion2(value);
9551
+ function moveReadonly(items, fromIndex, toIndex) {
9552
+ const moved = [...items];
9553
+ const [item] = moved.splice(fromIndex, 1);
9554
+ if (item === void 0) return items;
9555
+ moved.splice(toIndex, 0, item);
9556
+ return moved;
9118
9557
  }
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);
9558
+ var EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS = {
9559
+ bold: (value) => requireBooleanOrUndefined(value, "bold"),
9560
+ italic: (value) => requireBooleanOrUndefined(value, "italic"),
9561
+ underline: (value) => requireBooleanOrUndefined(value, "underline"),
9562
+ fontSize: (value) => {
9563
+ if (value !== void 0 && (!Number.isFinite(value) || value <= 0)) {
9564
+ throw new Error("updateTextRunProperties: fontSize must be a finite positive pt value");
9155
9565
  }
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);
9566
+ },
9567
+ color: (value) => {
9568
+ if (value === void 0) return;
9569
+ if (value.kind !== "srgb") {
9570
+ throw new Error("updateTextRunProperties: only srgb text run color is supported");
9162
9571
  }
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
- );
9572
+ if (!/^[0-9A-Fa-f]{6}$/.test(value.hex)) {
9573
+ throw new Error("updateTextRunProperties: srgb text run color must be a 6-digit hex value");
9574
+ }
9575
+ },
9576
+ typeface: (value) => {
9577
+ if (value !== void 0 && value.trim() === "") {
9578
+ throw new Error("updateTextRunProperties: typeface must be a non-empty string");
9177
9579
  }
9178
9580
  }
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 } : {}
9581
+ };
9582
+ var EDITABLE_TEXT_RUN_PROPERTIES = Object.keys(EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS).filter(
9583
+ (property) => property in EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS
9584
+ );
9585
+ var EDITABLE_TEXT_RUN_PROPERTY_SET = new Set(EDITABLE_TEXT_RUN_PROPERTIES);
9586
+ var EDITABLE_PARAGRAPH_PROPERTIES = [
9587
+ "align",
9588
+ "level",
9589
+ "bullet"
9590
+ ];
9591
+ var EDITABLE_PARAGRAPH_PROPERTY_SET = new Set(EDITABLE_PARAGRAPH_PROPERTIES);
9592
+ var TEXT_ALIGN_VALUES = /* @__PURE__ */ new Set([
9593
+ "left",
9594
+ "center",
9595
+ "right",
9596
+ "justify"
9597
+ ]);
9598
+ var AUTO_NUM_SCHEMES = /* @__PURE__ */ new Set([
9599
+ "arabicPeriod",
9600
+ "arabicParenR",
9601
+ "romanUcPeriod",
9602
+ "romanLcPeriod",
9603
+ "alphaUcPeriod",
9604
+ "alphaLcPeriod",
9605
+ "alphaLcParenR",
9606
+ "alphaUcParenR",
9607
+ "arabicPlain"
9608
+ ]);
9609
+ function replaceTextRunPlainText(source, handle, text) {
9610
+ const result = mapMatchingTextRun(
9611
+ source,
9612
+ handle,
9613
+ (run) => run.text === text ? run : { ...run, text }
9614
+ );
9615
+ if (!result.matched) {
9616
+ throw new Error(
9617
+ "replaceTextRunPlainText: text run handle was not found in PptxSourceModel source"
9618
+ );
9619
+ }
9620
+ if (!result.changed) return source;
9621
+ return {
9622
+ ...source,
9623
+ slides: result.slides,
9624
+ edits: [...source.edits ?? [], { kind: "replaceTextRunPlainText", handle, text }]
9192
9625
  };
9193
- return Object.keys(parsed).length > 0 ? parsed : void 0;
9194
9626
  }
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;
9627
+ function setTextRunProperties(source, handle, properties) {
9628
+ return updateTextRunProperties(source, handle, {
9629
+ set: properties,
9630
+ clear: []
9631
+ });
9212
9632
  }
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
- };
9633
+ function clearTextRunProperties(source, handle, properties) {
9634
+ return updateTextRunProperties(source, handle, {
9635
+ set: {},
9636
+ clear: properties
9637
+ });
9225
9638
  }
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
- };
9639
+ function setParagraphProperties(source, handle, properties) {
9640
+ return updateParagraphProperties(source, handle, {
9641
+ set: properties,
9642
+ clear: []
9643
+ });
9236
9644
  }
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;
9645
+ function clearParagraphProperties(source, handle, properties) {
9646
+ return updateParagraphProperties(source, handle, {
9647
+ set: {},
9648
+ clear: properties
9649
+ });
9650
+ }
9651
+ function replaceParagraphPlainText(source, handle, text) {
9652
+ const result = mapMatchingParagraph(source, handle, (paragraph) => {
9653
+ const replacementHandle = createReplacementRunHandle(paragraph);
9654
+ return {
9655
+ ...paragraph,
9656
+ runs: [
9657
+ {
9658
+ kind: "textRun",
9659
+ text,
9660
+ ...paragraph.runs[0]?.properties !== void 0 ? { properties: paragraph.runs[0].properties } : {},
9661
+ ...replacementHandle !== void 0 ? { handle: replacementHandle } : {}
9662
+ }
9663
+ ]
9664
+ };
9665
+ });
9666
+ if (!result.matched) {
9667
+ throw new Error(
9668
+ "replaceParagraphPlainText: paragraph handle was not found in PptxSourceModel source"
9669
+ );
9670
+ }
9241
9671
  return {
9242
- radius: asEmu2(numericAttr2(node, "rad") ?? 0),
9243
- color
9672
+ ...source,
9673
+ slides: result.slides,
9674
+ edits: [...source.edits ?? [], { kind: "replaceParagraphPlainText", handle, text }]
9244
9675
  };
9245
9676
  }
9246
- function parseSoftEdge(node) {
9247
- if (node === void 0) return void 0;
9677
+ function updateTextRunProperties(source, handle, patch) {
9678
+ assertEditableTextRunProperties(patch.set);
9679
+ assertEditableTextRunPropertyNames(patch.clear);
9680
+ const set = definedEditableTextRunProperties(patch.set);
9681
+ if (Object.values(set).every((value) => value === void 0) && patch.clear.length === 0) {
9682
+ throw new Error("updateTextRunProperties: patch must set or clear at least one property");
9683
+ }
9684
+ const result = mapMatchingTextRun(source, handle, (run) => {
9685
+ const properties = patchTextRunProperties(run.properties, { set, clear: patch.clear });
9686
+ if (textRunPropertiesEqual(run.properties, properties)) return run;
9687
+ return {
9688
+ kind: run.kind,
9689
+ text: run.text,
9690
+ ...run.handle !== void 0 ? { handle: run.handle } : {},
9691
+ ...run.rawSidecars !== void 0 ? { rawSidecars: run.rawSidecars } : {},
9692
+ ...properties !== void 0 ? { properties } : {}
9693
+ };
9694
+ });
9695
+ if (!result.matched) {
9696
+ throw new Error(
9697
+ "updateTextRunProperties: text run handle was not found in PptxSourceModel source"
9698
+ );
9699
+ }
9700
+ if (!result.changed) return source;
9248
9701
  return {
9249
- radius: asEmu2(numericAttr2(node, "rad") ?? 0)
9702
+ ...source,
9703
+ slides: result.slides,
9704
+ edits: [
9705
+ ...source.edits ?? [],
9706
+ {
9707
+ kind: "updateTextRunProperties",
9708
+ handle,
9709
+ ...Object.keys(set).length > 0 ? { set } : {},
9710
+ ...patch.clear.length > 0 ? { clear: patch.clear } : {}
9711
+ }
9712
+ ]
9250
9713
  };
9251
9714
  }
9252
- function parseBiLevel(node) {
9253
- if (node === void 0) return void 0;
9715
+ function updateParagraphProperties(source, handle, patch) {
9716
+ assertEditableParagraphProperties(patch.set);
9717
+ assertEditableParagraphPropertyNames(patch.clear);
9718
+ const set = definedEditableParagraphProperties(patch.set);
9719
+ if (Object.values(set).every((value) => value === void 0) && patch.clear.length === 0) {
9720
+ throw new Error("updateParagraphProperties: patch must set or clear at least one property");
9721
+ }
9722
+ const result = mapMatchingParagraph(source, handle, (paragraph) => {
9723
+ const properties = patchParagraphProperties(paragraph.properties, { set, clear: patch.clear });
9724
+ if (paragraphPropertiesEqual(paragraph.properties, properties)) return paragraph;
9725
+ return {
9726
+ runs: paragraph.runs,
9727
+ ...paragraph.handle !== void 0 ? { handle: paragraph.handle } : {},
9728
+ ...paragraph.rawSidecars !== void 0 ? { rawSidecars: paragraph.rawSidecars } : {},
9729
+ ...properties !== void 0 ? { properties } : {}
9730
+ };
9731
+ });
9732
+ if (!result.matched) {
9733
+ throw new Error(
9734
+ "updateParagraphProperties: paragraph handle was not found in PptxSourceModel source"
9735
+ );
9736
+ }
9737
+ if (!result.changed) return source;
9254
9738
  return {
9255
- threshold: (numericAttr2(node, "thresh") ?? 5e4) / 1e5
9739
+ ...source,
9740
+ slides: result.slides,
9741
+ edits: [
9742
+ ...source.edits ?? [],
9743
+ {
9744
+ kind: "updateParagraphProperties",
9745
+ handle,
9746
+ ...Object.keys(set).length > 0 ? { set } : {},
9747
+ ...patch.clear.length > 0 ? { clear: patch.clear } : {}
9748
+ }
9749
+ ]
9256
9750
  };
9257
9751
  }
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
- };
9752
+ function mapMatchingTextRun(source, handle, mapRun) {
9753
+ return mapTextBodyParagraphs(source, (paragraph) => {
9754
+ let matched = false;
9755
+ let changed = false;
9756
+ const runs = paragraph.runs.map((run) => {
9757
+ if (!sourceHandlesEqual(run.handle, handle)) return run;
9758
+ matched = true;
9759
+ const mapped = mapRun(run);
9760
+ if (mapped === run) return run;
9761
+ changed = true;
9762
+ return mapped;
9763
+ });
9764
+ return {
9765
+ paragraph: changed ? { ...paragraph, runs } : paragraph,
9766
+ matched
9767
+ };
9768
+ });
9264
9769
  }
9265
- function parseLumEffect(node) {
9266
- if (node === void 0) return void 0;
9770
+ function mapMatchingParagraph(source, handle, mapParagraph) {
9771
+ return mapTextBodyParagraphs(source, (paragraph) => {
9772
+ if (!sourceHandlesEqual(paragraph.handle, handle)) {
9773
+ return { paragraph, matched: false };
9774
+ }
9775
+ return { paragraph: mapParagraph(paragraph), matched: true };
9776
+ });
9777
+ }
9778
+ function mapTextBodyParagraphs(source, mapParagraph) {
9779
+ let matched = false;
9780
+ let changed = false;
9781
+ const slides = source.slides.map((slide) => {
9782
+ let slideChanged = false;
9783
+ const shapes = slide.shapes.map((shape) => {
9784
+ if (shape.kind !== "shape" || shape.textBody === void 0) return shape;
9785
+ let shapeChanged = false;
9786
+ const paragraphs = shape.textBody.paragraphs.map((paragraph) => {
9787
+ const result = mapParagraph(paragraph);
9788
+ if (result.matched) matched = true;
9789
+ if (result.paragraph === paragraph) return paragraph;
9790
+ changed = true;
9791
+ shapeChanged = true;
9792
+ slideChanged = true;
9793
+ return result.paragraph;
9794
+ });
9795
+ if (!shapeChanged) return shape;
9796
+ return {
9797
+ ...shape,
9798
+ textBody: {
9799
+ ...shape.textBody,
9800
+ paragraphs
9801
+ }
9802
+ };
9803
+ });
9804
+ return slideChanged ? { ...slide, shapes } : slide;
9805
+ });
9267
9806
  return {
9268
- brightness: (numericAttr2(node, "bright") ?? 0) / 1e5,
9269
- contrast: (numericAttr2(node, "contrast") ?? 0) / 1e5
9807
+ slides: changed ? slides : source.slides,
9808
+ matched,
9809
+ changed
9270
9810
  };
9271
9811
  }
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
- }
9812
+ function patchTextRunProperties(current, patch) {
9813
+ const next = { ...current ?? {} };
9814
+ for (const property of patch.clear) {
9815
+ delete next[property];
9294
9816
  }
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;
9817
+ Object.assign(next, patch.set);
9818
+ return Object.keys(next).length > 0 ? next : void 0;
9311
9819
  }
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) };
9820
+ function patchParagraphProperties(current, patch) {
9821
+ const next = { ...current ?? {} };
9822
+ for (const property of patch.clear) {
9823
+ delete next[property];
9329
9824
  }
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) };
9825
+ Object.assign(next, patch.set);
9826
+ return Object.keys(next).length > 0 ? next : void 0;
9827
+ }
9828
+ function assertEditableTextRunProperties(properties) {
9829
+ for (const property of Object.keys(properties)) {
9830
+ assertEditableTextRunPropertyName(property);
9334
9831
  }
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
- }
9832
+ for (const property of EDITABLE_TEXT_RUN_PROPERTIES) {
9833
+ validateEditableTextRunProperty(property, properties[property]);
9339
9834
  }
9340
- return void 0;
9341
9835
  }
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
- });
9836
+ function assertEditableParagraphProperties(properties) {
9837
+ for (const property of Object.keys(properties)) {
9838
+ assertEditableParagraphPropertyName(property);
9351
9839
  }
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
- };
9840
+ validateParagraphAlign(properties.align);
9841
+ validateParagraphLevel(properties.level);
9842
+ validateParagraphBullet(properties.bullet);
9843
+ }
9844
+ function requireBooleanOrUndefined(value, fieldName) {
9845
+ if (value !== void 0 && typeof value !== "boolean") {
9846
+ throw new Error(`updateTextRunProperties: ${fieldName} must be a boolean value`);
9367
9847
  }
9368
- return {
9369
- kind: "gradient",
9370
- gradientType: "linear",
9371
- stops,
9372
- angle: asOoxmlAngle(numericAttr2(getChild(grad, "lin"), "ang") ?? 0)
9373
- };
9374
9848
  }
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
- };
9849
+ function assertEditableTextRunPropertyNames(properties) {
9850
+ for (const property of properties) {
9851
+ assertEditableTextRunPropertyName(property);
9852
+ }
9385
9853
  }
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
- };
9854
+ function assertEditableParagraphPropertyNames(properties) {
9855
+ for (const property of properties) {
9856
+ assertEditableParagraphPropertyName(property);
9857
+ }
9397
9858
  }
9398
- function parseRectangleAlignment(value, fallback) {
9399
- return parseEnumValueWithDefault(value, RECTANGLE_ALIGNMENT_TOKENS, fallback);
9859
+ function definedEditableTextRunProperties(properties) {
9860
+ const defined = {};
9861
+ for (const property of EDITABLE_TEXT_RUN_PROPERTIES) {
9862
+ copyDefinedEditableTextRunProperty(defined, properties, property);
9863
+ }
9864
+ return defined;
9400
9865
  }
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
- };
9866
+ function definedEditableParagraphProperties(properties) {
9867
+ const defined = {};
9868
+ for (const property of EDITABLE_PARAGRAPH_PROPERTIES) {
9869
+ copyDefinedEditableParagraphProperty(defined, properties, property);
9870
+ }
9871
+ return defined;
9411
9872
  }
9412
- function parseOutline(spPr, nextId) {
9413
- const ln = getChild(spPr, "ln");
9414
- return parseLine(ln, nextId);
9873
+ function assertEditableTextRunPropertyName(property) {
9874
+ if (!isEditableTextRunProperty(property)) {
9875
+ throw new Error(`updateTextRunProperties: unsupported text run property '${property}'`);
9876
+ }
9415
9877
  }
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
- };
9878
+ function assertEditableParagraphPropertyName(property) {
9879
+ if (!EDITABLE_PARAGRAPH_PROPERTY_SET.has(property)) {
9880
+ throw new Error(`updateParagraphProperties: unsupported paragraph property '${property}'`);
9881
+ }
9436
9882
  }
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;
9883
+ function isEditableTextRunProperty(property) {
9884
+ return EDITABLE_TEXT_RUN_PROPERTY_SET.has(property);
9885
+ }
9886
+ function validateParagraphAlign(value) {
9887
+ if (value !== void 0 && !TEXT_ALIGN_VALUES.has(value)) {
9888
+ throw new Error("updateParagraphProperties: align must be left, center, right, or justify");
9448
9889
  }
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
9890
  }
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
- });
9891
+ function validateParagraphLevel(value) {
9892
+ if (value !== void 0 && (!Number.isInteger(value) || value < 0 || value > 8)) {
9893
+ throw new Error("updateParagraphProperties: level must be an integer from 0 to 8");
9894
+ }
9895
+ }
9896
+ function validateParagraphBullet(value) {
9897
+ if (value === void 0) return;
9898
+ if (value.type === "none") return;
9899
+ if (value.type === "char") {
9900
+ if (typeof value.char !== "string" || value.char.length === 0) {
9901
+ throw new Error("updateParagraphProperties: bullet.char must be a non-empty string");
9480
9902
  }
9903
+ return;
9481
9904
  }
9482
- return transforms.length > 0 ? { ...base, transforms } : base;
9905
+ if (value.type === "autoNum") {
9906
+ if (!AUTO_NUM_SCHEMES.has(value.scheme)) {
9907
+ throw new Error("updateParagraphProperties: unsupported bullet auto-numbering scheme");
9908
+ }
9909
+ if (!Number.isInteger(value.startAt) || value.startAt < 1) {
9910
+ throw new Error("updateParagraphProperties: bullet.startAt must be a positive integer");
9911
+ }
9912
+ return;
9913
+ }
9914
+ throw new Error("updateParagraphProperties: unsupported bullet type");
9483
9915
  }
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;
9916
+ function validateEditableTextRunProperty(property, value) {
9917
+ EDITABLE_TEXT_RUN_PROPERTY_VALIDATORS[property](value);
9489
9918
  }
9490
- function isTrue(value) {
9491
- return value === "1" || value === "true";
9919
+ function copyDefinedEditableParagraphProperty(target, source, property) {
9920
+ const value = source[property];
9921
+ if (value !== void 0) {
9922
+ target[property] = value;
9923
+ }
9492
9924
  }
9493
- function parseDashStyle(prstDash) {
9494
- const value = getAttr(prstDash, "val");
9495
- return parseEnumValue(value, DASH_STYLES);
9925
+ function copyDefinedEditableTextRunProperty(target, source, property) {
9926
+ const value = source[property];
9927
+ if (value !== void 0) {
9928
+ target[property] = value;
9929
+ }
9496
9930
  }
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
- ]);
9931
+ function textRunPropertiesEqual(left, right) {
9932
+ return stableValueEqual2(left ?? {}, right ?? {});
9505
9933
  }
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;
9934
+ function paragraphPropertiesEqual(left, right) {
9935
+ return stableValueEqual2(left ?? {}, right ?? {});
9513
9936
  }
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;
9937
+ function stableValueEqual2(left, right) {
9938
+ if (Object.is(left, right)) return true;
9939
+ if (Array.isArray(left) || Array.isArray(right)) {
9940
+ if (!Array.isArray(left) || !Array.isArray(right)) return false;
9941
+ if (left.length !== right.length) return false;
9942
+ return left.every((value, index) => stableValueEqual2(value, right[index]));
9943
+ }
9944
+ if (isPlainRecord2(left) || isPlainRecord2(right)) {
9945
+ if (!isPlainRecord2(left) || !isPlainRecord2(right)) return false;
9946
+ const leftKeys = Object.keys(left).sort();
9947
+ const rightKeys = Object.keys(right).sort();
9948
+ if (!stableValueEqual2(leftKeys, rightKeys)) return false;
9949
+ return leftKeys.every((key) => stableValueEqual2(left[key], right[key]));
9950
+ }
9951
+ return false;
9519
9952
  }
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";
9953
+ function isPlainRecord2(value) {
9954
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9955
+ }
9956
+ function createReplacementRunHandle(paragraph) {
9957
+ if (paragraph.runs[0]?.handle !== void 0) return paragraph.runs[0].handle;
9958
+ if (paragraph.handle?.nodeId === void 0) return void 0;
9526
9959
  return {
9527
- type,
9528
- width: parseEnumValueWithDefault(width, ARROW_SIZES2, "med"),
9529
- length: parseEnumValueWithDefault(length, ARROW_SIZES2, "med")
9960
+ ...paragraph.handle,
9961
+ nodeId: asSourceNodeId(`${paragraph.handle.nodeId}:r:0`),
9962
+ orderingSlot: 0
9530
9963
  };
9531
9964
  }
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"]);
9965
+ function asEmu2(value) {
9966
+ return unsafeBrandAssertion2(value);
9967
+ }
9968
+ function asPt(value) {
9969
+ return unsafeBrandAssertion2(value);
9970
+ }
9971
+ function asHundredthPt2(value) {
9972
+ return unsafeBrandAssertion2(value);
9973
+ }
9974
+ function asOoxmlPercent(value) {
9975
+ return unsafeBrandAssertion2(value);
9976
+ }
9977
+ function asOoxmlAngle(value) {
9978
+ return unsafeBrandAssertion2(value);
9979
+ }
9980
+ var textEncoder = new TextEncoder();
9981
+ var ROOT_PART = asPartPath("");
9982
+ var PRESENTATION_PART = asPartPath("ppt/presentation.xml");
9983
+ var SLIDE_PART = asPartPath("ppt/slides/slide1.xml");
9984
+ var SLIDE_LAYOUT_PART = asPartPath("ppt/slideLayouts/slideLayout1.xml");
9985
+ var SLIDE_MASTER_PART = asPartPath("ppt/slideMasters/slideMaster1.xml");
9986
+ var THEME_PART = asPartPath("ppt/theme/theme1.xml");
9987
+ var APP_PROPS_PART = asPartPath("docProps/app.xml");
9988
+ var CORE_PROPS_PART = asPartPath("docProps/core.xml");
9550
9989
  var DEFAULT_COLOR_MAP = {
9551
9990
  bg1: "lt1",
9552
9991
  tx1: "dk1",
@@ -10110,7 +10549,7 @@ function computeConnectorElement(context, connector, layer, partPath) {
10110
10549
  sourceNode: connector,
10111
10550
  ...connector.transform !== void 0 ? { transform: connector.transform } : {},
10112
10551
  ...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) } : {},
10552
+ ...computedOutlineProperty(context, connector.outline, connector.style?.lineRef, partPath),
10114
10553
  ...effects !== void 0 ? { effects } : {}
10115
10554
  };
10116
10555
  }
@@ -10154,7 +10593,7 @@ function computeShapeElement(context, shape, layer, partPath) {
10154
10593
  ...transform !== void 0 ? { transform } : {},
10155
10594
  ...geometry !== void 0 ? { geometry } : {},
10156
10595
  ...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) } : {},
10596
+ ...computedOutlineProperty(context, shape.outline, shape.style?.lineRef, partPath),
10158
10597
  ...effects !== void 0 ? { effects } : {},
10159
10598
  ...shape.textBody !== void 0 ? {
10160
10599
  textBody: computeTextBody(
@@ -10169,7 +10608,7 @@ function computeShapeElement(context, shape, layer, partPath) {
10169
10608
  }
10170
10609
  function computeImageElement(context, image, layer, partPath) {
10171
10610
  const relationship = resolveComputedRelationships(context.source, partPath).find(
10172
- (rel) => rel.id === image.blipRelationshipId && rel.type === IMAGE_REL_TYPE2
10611
+ (rel2) => rel2.id === image.blipRelationshipId && rel2.type === IMAGE_REL_TYPE2
10173
10612
  );
10174
10613
  const effects = image.effects !== void 0 ? computeEffectList(context, image.effects) : void 0;
10175
10614
  const blipEffects = image.blipEffects !== void 0 ? computeBlipEffects(context, image.blipEffects) : void 0;
@@ -10200,7 +10639,7 @@ function computeTableElement(context, table, layer, partPath) {
10200
10639
  }
10201
10640
  function computeChartElement(context, chart, layer, partPath) {
10202
10641
  const relationship = context.relationships.find(
10203
- (rel) => rel.id === chart.chartRelationshipId && CHART_REL_TYPES.has(rel.type)
10642
+ (rel2) => rel2.id === chart.chartRelationshipId && CHART_REL_TYPES.has(rel2.type)
10204
10643
  );
10205
10644
  const chartXml = relationship?.targetPartPath !== void 0 ? readRawPackageText(context.source, relationship.targetPartPath) : void 0;
10206
10645
  const chartData = chartXml !== void 0 ? parseComputedChartData(chartXml, context) : void 0;
@@ -10217,11 +10656,11 @@ function computeChartElement(context, chart, layer, partPath) {
10217
10656
  }
10218
10657
  function computeSmartArtElement(context, smartArt, layer, partPath) {
10219
10658
  const dataRelationship = context.relationships.find(
10220
- (rel) => rel.id === smartArt.dataRelationshipId && DIAGRAM_DATA_REL_TYPES.has(rel.type)
10659
+ (rel2) => rel2.id === smartArt.dataRelationshipId && DIAGRAM_DATA_REL_TYPES.has(rel2.type)
10221
10660
  );
10222
10661
  const dataRelationships = dataRelationship?.targetPartPath !== void 0 ? resolveComputedRelationships(context.source, dataRelationship.targetPartPath) : [];
10223
10662
  const drawingRelationship = dataRelationships.find(
10224
- (rel) => DIAGRAM_DRAWING_REL_TYPES.has(rel.type)
10663
+ (rel2) => DIAGRAM_DRAWING_REL_TYPES.has(rel2.type)
10225
10664
  );
10226
10665
  const drawingPartPath = drawingRelationship?.targetPartPath;
10227
10666
  const drawingXml = drawingPartPath !== void 0 ? readRawPackageText(context.source, drawingPartPath) : void 0;
@@ -10367,7 +10806,7 @@ function computeFill(context, fill, partPath) {
10367
10806
  };
10368
10807
  case "image": {
10369
10808
  const relationship = resolveComputedRelationships(context.source, partPath).find(
10370
- (rel) => rel.id === fill.blipRelationshipId && rel.type === IMAGE_REL_TYPE2
10809
+ (rel2) => rel2.id === fill.blipRelationshipId && rel2.type === IMAGE_REL_TYPE2
10371
10810
  );
10372
10811
  return {
10373
10812
  kind: "image",
@@ -10392,6 +10831,24 @@ function computeOutline(context, outline, partPath) {
10392
10831
  ...outline.fill !== void 0 ? { fill: computeFill(context, outline.fill, partPath) } : {}
10393
10832
  };
10394
10833
  }
10834
+ function computedOutlineProperty(context, outline, lineRef, partPath) {
10835
+ const styleOutline = lineRef !== void 0 ? resolveLineReference(context, lineRef, partPath) : void 0;
10836
+ if (outline === void 0) {
10837
+ return styleOutline !== void 0 ? { outline: styleOutline } : {};
10838
+ }
10839
+ const computed = mergeComputedOutline(styleOutline, computeOutline(context, outline, partPath));
10840
+ return { outline: computed };
10841
+ }
10842
+ function mergeComputedOutline(base, override) {
10843
+ if (base === void 0) return override;
10844
+ const width = override.width ?? base.width;
10845
+ const fill = override.fill ?? base.fill;
10846
+ return {
10847
+ source: { ...base.source, ...override.source },
10848
+ ...width !== void 0 ? { width } : {},
10849
+ ...fill !== void 0 ? { fill } : {}
10850
+ };
10851
+ }
10395
10852
  function resolveFillReference(context, ref, partPath) {
10396
10853
  if (ref.index === 0) return void 0;
10397
10854
  const list = ref.index >= 1e3 ? context.theme?.formatScheme?.backgroundFillStyles : context.theme?.formatScheme?.fillStyles;
@@ -10980,7 +11437,7 @@ var SLIDE_REL_TYPE2 = "http://schemas.openxmlformats.org/officeDocument/2006/rel
10980
11437
  var SLIDE_LAYOUT_REL_TYPE2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
10981
11438
  var SLIDE_MASTER_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
10982
11439
  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";
11440
+ var PRESENTATION_CONTENT_TYPE2 = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml";
10984
11441
  var textDecoder3 = new TextDecoder();
10985
11442
  function readPptx(input) {
10986
11443
  const entries = unzipPackage(input);
@@ -11147,15 +11604,15 @@ function parsePartRoot(entries, partPath, rootLocalName, diagnostics, includeOrd
11147
11604
  return { root, orderedRoot };
11148
11605
  }
11149
11606
  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");
11607
+ const rels = relationships.find((rel2) => rel2.sourcePartPath === sourcePart)?.relationships;
11608
+ const match = rels?.find((rel2) => rel2.type === relType && rel2.targetMode !== "External");
11152
11609
  if (match === void 0) return void 0;
11153
11610
  return resolveInternalRelationshipTarget(sourcePart, match);
11154
11611
  }
11155
11612
  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);
11613
+ const rels = relationships.find((rel2) => rel2.sourcePartPath === sourcePart)?.relationships ?? [];
11614
+ return rels.filter((rel2) => rel2.type === relType && rel2.targetMode !== "External").flatMap((rel2) => {
11615
+ const target = resolveInternalRelationshipTarget(sourcePart, rel2);
11159
11616
  return target === void 0 ? [] : [target];
11160
11617
  });
11161
11618
  }
@@ -11244,7 +11701,7 @@ function readPresentation(entries, relationships, overrides, diagnostics) {
11244
11701
  const slideSize = readSlideSize(root);
11245
11702
  const defaultTextStyle = parseTextStyle(getChild(root, "defaultTextStyle"));
11246
11703
  const presentationRels = relationships.find(
11247
- (rel) => rel.sourcePartPath === presentationPath
11704
+ (rel2) => rel2.sourcePartPath === presentationPath
11248
11705
  )?.relationships;
11249
11706
  const slidePartPaths = [];
11250
11707
  const sldIdLst = getChild(root, "sldIdLst");
@@ -11252,7 +11709,7 @@ function readPresentation(entries, relationships, overrides, diagnostics) {
11252
11709
  const relId = getNamespacedAttr(sldId, "id");
11253
11710
  if (relId === void 0) continue;
11254
11711
  const handle = { partPath: presentationPartPath, relationshipId: asRelationshipId(relId) };
11255
- const relationship = presentationRels?.find((rel) => rel.id === relId);
11712
+ const relationship = presentationRels?.find((rel2) => rel2.id === relId);
11256
11713
  if (relationship === void 0) {
11257
11714
  diagnostics.push({
11258
11715
  severity: "warning",
@@ -11295,15 +11752,15 @@ function readSlideSize(presentationRoot) {
11295
11752
  }
11296
11753
  function locatePresentationPart(relationships, overrides) {
11297
11754
  const rootRels = relationships.find(
11298
- (rel) => rel.sourcePartPath === PACKAGE_ROOT_PART
11755
+ (rel2) => rel2.sourcePartPath === PACKAGE_ROOT_PART
11299
11756
  )?.relationships;
11300
11757
  const officeDocumentRel = rootRels?.find(
11301
- (rel) => rel.type === OFFICE_DOCUMENT_REL_TYPE && rel.targetMode !== "External"
11758
+ (rel2) => rel2.type === OFFICE_DOCUMENT_REL_TYPE && rel2.targetMode !== "External"
11302
11759
  );
11303
11760
  if (officeDocumentRel !== void 0) {
11304
11761
  return resolveRelationshipTarget(PACKAGE_ROOT_PART, officeDocumentRel.target);
11305
11762
  }
11306
- const override = overrides.find((entry) => entry.contentType === PRESENTATION_CONTENT_TYPE);
11763
+ const override = overrides.find((entry) => entry.contentType === PRESENTATION_CONTENT_TYPE2);
11307
11764
  return override?.partName;
11308
11765
  }
11309
11766
  var MEDIA_CONTENT_TYPE_PREFIXES = ["image/", "audio/", "video/"];
@@ -11331,9 +11788,9 @@ function extensionOf(path) {
11331
11788
  function stripLeadingSlash(path) {
11332
11789
  return path.startsWith("/") ? path.slice(1) : path;
11333
11790
  }
11334
- function parseShapeLocator(handle) {
11791
+ function parseShapeLocator(handle, editName = "shape edit") {
11335
11792
  if (handle.nodeId !== void 0) return { nodeId: String(handle.nodeId) };
11336
- throw new Error("writePptx: shape transform edit requires nodeId in handle");
11793
+ throw new Error(`writePptx: ${editName} requires nodeId in handle`);
11337
11794
  }
11338
11795
  function parseTextRunLocator(nodeId) {
11339
11796
  const value = String(nodeId ?? "");
@@ -11400,19 +11857,21 @@ function locateShapeTreeNode(spTree, locator) {
11400
11857
  function deleteShapeXml(spTree, nodeId) {
11401
11858
  if (spTree === void 0) return false;
11402
11859
  const entry = Object.entries(spTree).find(
11403
- ([key2]) => !key2.startsWith("@_") && localName(key2) === "sp"
11860
+ ([key2, value2]) => !key2.startsWith("@_") && (localName(key2) === "sp" || localName(key2) === "cxnSp") && getShapeTreeNodes(value2).some((shape) => getShapeTreeNodeId(shape) === nodeId)
11404
11861
  );
11405
11862
  if (entry === void 0) return false;
11406
11863
  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
- );
11864
+ const shapes = getShapeTreeNodes(value);
11865
+ const nextShapes = shapes.filter((shape) => getShapeTreeNodeId(shape) !== nodeId);
11411
11866
  if (nextShapes.length === shapes.length) return false;
11412
11867
  if (nextShapes.length === 0) delete spTree[key];
11413
11868
  else spTree[key] = Array.isArray(value) ? nextShapes : nextShapes[0];
11414
11869
  return true;
11415
11870
  }
11871
+ function getShapeTreeNodes(value) {
11872
+ const items = Array.isArray(value) ? unsafeOoxmlBoundaryAssertion(value) : [value];
11873
+ return items.map((item) => unsafeOoxmlBoundaryAssertion(item));
11874
+ }
11416
11875
  function getShapeByOrderingSlot(spTree, orderingSlot) {
11417
11876
  if (!spTree) return void 0;
11418
11877
  let currentSlot = 0;
@@ -11551,7 +12010,7 @@ function textRequiresPreserve(text) {
11551
12010
  return text.startsWith(" ") || text.endsWith(" ");
11552
12011
  }
11553
12012
  var XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
11554
- var textEncoder = new TextEncoder();
12013
+ var textEncoder2 = new TextEncoder();
11555
12014
  var textDecoder4 = new TextDecoder();
11556
12015
  var xmlBuilder2 = new import_fast_xml_parser3.XMLBuilder({
11557
12016
  ignoreAttributes: false,
@@ -11560,8 +12019,16 @@ var xmlBuilder2 = new import_fast_xml_parser3.XMLBuilder({
11560
12019
  suppressEmptyNode: true
11561
12020
  });
11562
12021
  function encodeXml(xml) {
11563
- return textEncoder.encode(xml);
12022
+ return textEncoder2.encode(xml);
11564
12023
  }
12024
+ var FILL_CHILD_LOCAL_NAMES = /* @__PURE__ */ new Set([
12025
+ "noFill",
12026
+ "solidFill",
12027
+ "gradFill",
12028
+ "blipFill",
12029
+ "pattFill",
12030
+ "grpFill"
12031
+ ]);
11565
12032
  function serializeDirtyXmlPart(source, partPath, edits) {
11566
12033
  const rawPart = source.packageGraph.rawParts?.find((part) => part.partPath === partPath);
11567
12034
  if (rawPart === void 0) {
@@ -11585,12 +12052,21 @@ function applyDirtyPartEdit(root, edit) {
11585
12052
  case "updateTextRunProperties":
11586
12053
  applyTextRunPropertiesEdit(root, edit);
11587
12054
  return;
12055
+ case "updateParagraphProperties":
12056
+ applyParagraphPropertiesEdit(root, edit);
12057
+ return;
11588
12058
  case "replaceParagraphPlainText":
11589
12059
  applyParagraphTextEdit(root, edit);
11590
12060
  return;
11591
12061
  case "updateShapeTransform":
11592
12062
  applyShapeTransformEdit(root, edit);
11593
12063
  return;
12064
+ case "updateShapeFill":
12065
+ applyShapeFillEdit(root, edit);
12066
+ return;
12067
+ case "updateShapeOutline":
12068
+ applyShapeOutlineEdit(root, edit);
12069
+ return;
11594
12070
  case "addTextBox":
11595
12071
  applyAddTextBoxEdit(root, edit);
11596
12072
  return;
@@ -11603,6 +12079,7 @@ function applyDirtyPartEdit(root, edit) {
11603
12079
  case "replaceImage":
11604
12080
  case "addEmptySlideFromLayout":
11605
12081
  case "duplicateSlide":
12082
+ case "moveSlide":
11606
12083
  case "deleteSlide":
11607
12084
  throw new Error(`writePptx: edit kind '${edit.kind}' does not patch a dirty XML part`);
11608
12085
  }
@@ -11669,6 +12146,34 @@ function applyParagraphTextEdit(root, edit) {
11669
12146
  }
11670
12147
  replaceParagraphRunsWithSingleTextRun(paragraph, edit.text);
11671
12148
  }
12149
+ function applyParagraphPropertiesEdit(root, edit) {
12150
+ assertParagraphPropertiesEdit(edit);
12151
+ const locator = parseParagraphLocator(edit.handle.nodeId);
12152
+ const slide = getChild(root, "sld");
12153
+ const cSld = getChild(slide, "cSld");
12154
+ const spTree = getChild(cSld, "spTree");
12155
+ const shape = locateShape(spTree, locator);
12156
+ const paragraphs = getChildArray(getChild(shape, "txBody"), "p");
12157
+ const target = locateParagraphPropertiesForEdit(paragraphs, locator);
12158
+ if (target === void 0) {
12159
+ throw new Error(
12160
+ `writePptx: paragraph properties handle '${edit.handle.nodeId}' no longer matches source XML`
12161
+ );
12162
+ }
12163
+ const set = edit.set ?? {};
12164
+ const hasSet = hasParagraphPropertiesSetValues(set);
12165
+ const existingParagraphProperties = target.properties;
12166
+ if (existingParagraphProperties === void 0 && !hasSet) return;
12167
+ const pPr = existingParagraphProperties ?? ensureParagraphProperties(target.paragraph);
12168
+ let cleared = false;
12169
+ for (const property of edit.clear ?? []) {
12170
+ cleared = clearParagraphProperty(pPr, property) || cleared;
12171
+ }
12172
+ if (set.align !== void 0) pPr["@_algn"] = paragraphAlignOoxmlValue(set.align);
12173
+ if (set.level !== void 0) pPr["@_lvl"] = String(set.level);
12174
+ if (set.bullet !== void 0) setParagraphBullet(pPr, set.bullet);
12175
+ if (!hasSet && cleared && xmlNodeIsEmpty(pPr)) deleteParagraphProperties(target.paragraph, pPr);
12176
+ }
11672
12177
  function locateTextRun(root, nodeId) {
11673
12178
  const locator = parseTextRunLocator(nodeId);
11674
12179
  const slide = getChild(root, "sld");
@@ -11680,7 +12185,7 @@ function locateTextRun(root, nodeId) {
11680
12185
  return getChildArray(paragraph, "r")[locator.runIndex];
11681
12186
  }
11682
12187
  function applyShapeTransformEdit(root, edit) {
11683
- const locator = parseShapeLocator(edit.handle);
12188
+ const locator = parseShapeLocator(edit.handle, "shape transform edit");
11684
12189
  const slide = getChild(root, "sld");
11685
12190
  const cSld = getChild(slide, "cSld");
11686
12191
  const spTree = getChild(cSld, "spTree");
@@ -11703,6 +12208,22 @@ function applyShapeTransformEdit(root, edit) {
11703
12208
  ext["@_cx"] = String(edit.width);
11704
12209
  ext["@_cy"] = String(edit.height);
11705
12210
  }
12211
+ function applyShapeFillEdit(root, edit) {
12212
+ const shape = locateEditableShapeTreeNode(root, edit.handle, "shape fill edit");
12213
+ if (shape.localName === "cxnSp") {
12214
+ throw new Error(
12215
+ `writePptx: shape fill handle '${String(edit.handle.nodeId)}' references a connector`
12216
+ );
12217
+ }
12218
+ const spPr = ensureShapeProperties(shape.node);
12219
+ replaceFillChild(spPr, edit.fill);
12220
+ }
12221
+ function applyShapeOutlineEdit(root, edit) {
12222
+ const shape = locateEditableShapeTreeNode(root, edit.handle, "shape outline edit");
12223
+ const spPr = ensureShapeProperties(shape.node);
12224
+ const ln = ensureLineProperties(spPr);
12225
+ applyOutlinePatch(ln, edit.outline);
12226
+ }
11706
12227
  function applyAddTextBoxEdit(root, edit) {
11707
12228
  const slide = getChild(root, "sld");
11708
12229
  const cSld = getChild(slide, "cSld");
@@ -11725,22 +12246,133 @@ function applyAddConnectorEdit(root, edit) {
11725
12246
  if (locateShapeTreeNode(spTree, { nodeId: edit.shapeId }) !== void 0) {
11726
12247
  throw new Error(`writePptx: shape id '${edit.shapeId}' already exists in source XML`);
11727
12248
  }
11728
- if (locateShapeTreeNode(spTree, { nodeId: edit.startShapeId }) === void 0) {
12249
+ if (edit.startShapeId !== void 0 && locateShapeTreeNode(spTree, { nodeId: edit.startShapeId }) === void 0) {
11729
12250
  throw new Error(`writePptx: connector start shape '${edit.startShapeId}' was not found`);
11730
12251
  }
11731
- if (locateShapeTreeNode(spTree, { nodeId: edit.endShapeId }) === void 0) {
12252
+ if (edit.endShapeId !== void 0 && locateShapeTreeNode(spTree, { nodeId: edit.endShapeId }) === void 0) {
11732
12253
  throw new Error(`writePptx: connector end shape '${edit.endShapeId}' was not found`);
11733
12254
  }
11734
- appendChild(spTree, "p:cxnSp", parseShapeFragmentXml(edit.xml, "cxnSp"));
12255
+ appendChild(spTree, "p:cxnSp", parseShapeFragmentXml(edit.xml, "cxnSp"));
12256
+ }
12257
+ function applyDeleteShapeEdit(root, edit) {
12258
+ const locator = parseShapeLocator(edit.handle, "shape delete edit");
12259
+ const slide = getChild(root, "sld");
12260
+ const cSld = getChild(slide, "cSld");
12261
+ const spTree = getChild(cSld, "spTree");
12262
+ if (!deleteShapeXml(spTree, locator.nodeId)) {
12263
+ throw new Error(
12264
+ `writePptx: shape delete handle '${locator.nodeId}' no longer matches p:sp or p:cxnSp`
12265
+ );
12266
+ }
12267
+ }
12268
+ function locateEditableShapeTreeNode(root, handle, editName) {
12269
+ const locator = parseShapeLocator(handle, editName);
12270
+ const slide = getChild(root, "sld");
12271
+ const cSld = getChild(slide, "cSld");
12272
+ const spTree = getChild(cSld, "spTree");
12273
+ const shape = locateShapeTreeNodeWithLocalName(spTree, locator.nodeId);
12274
+ if (shape === void 0) {
12275
+ throw new Error(
12276
+ `writePptx: ${editName} handle '${String(handle.nodeId)}' no longer matches source XML`
12277
+ );
12278
+ }
12279
+ if (shape.localName !== "sp" && shape.localName !== "cxnSp") {
12280
+ throw new Error(
12281
+ `writePptx: ${editName} handle '${String(handle.nodeId)}' does not reference p:sp or p:cxnSp`
12282
+ );
12283
+ }
12284
+ return shape;
12285
+ }
12286
+ function locateShapeTreeNodeWithLocalName(spTree, nodeId) {
12287
+ if (spTree === void 0) return void 0;
12288
+ for (const key of Object.keys(spTree)) {
12289
+ if (key.startsWith("@_")) continue;
12290
+ const keyLocalName = localName(key);
12291
+ const value = spTree[key];
12292
+ const items = Array.isArray(value) ? value : [value];
12293
+ for (const item of items) {
12294
+ const node = unsafeOoxmlBoundaryAssertion(item);
12295
+ const nonVisualProperties = getChild(node, "nvSpPr") ?? getChild(node, "nvPicPr") ?? getChild(node, "nvCxnSpPr") ?? getChild(node, "nvGrpSpPr") ?? getChild(node, "nvGraphicFramePr");
12296
+ if (getChild(nonVisualProperties, "cNvPr")?.["@_id"] === nodeId) {
12297
+ return { node, localName: keyLocalName };
12298
+ }
12299
+ }
12300
+ }
12301
+ return void 0;
12302
+ }
12303
+ function ensureShapeProperties(shape) {
12304
+ const existing = getChild(shape, "spPr");
12305
+ if (existing !== void 0) return existing;
12306
+ const entries = [];
12307
+ let inserted = false;
12308
+ for (const [key, value] of Object.entries(shape)) {
12309
+ entries.push([key, value]);
12310
+ if (!inserted && !key.startsWith("@_") && localName(key).startsWith("nv")) {
12311
+ entries.push(["p:spPr", {}]);
12312
+ inserted = true;
12313
+ }
12314
+ }
12315
+ if (!inserted) entries.push(["p:spPr", {}]);
12316
+ replaceNodeEntries(shape, entries);
12317
+ return getChild(shape, "spPr") ?? {};
11735
12318
  }
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`);
12319
+ function ensureLineProperties(spPr) {
12320
+ const existing = getChild(spPr, "ln");
12321
+ if (existing !== void 0) return existing;
12322
+ insertChildByOrder(
12323
+ spPr,
12324
+ "a:ln",
12325
+ {},
12326
+ (name) => ["effectLst", "effectDag", "scene3d", "sp3d", "extLst"].includes(name)
12327
+ );
12328
+ return getChild(spPr, "ln") ?? {};
12329
+ }
12330
+ function applyOutlinePatch(ln, outline) {
12331
+ if (outline.width !== void 0) ln["@_w"] = String(outline.width);
12332
+ if (outline.fill !== void 0) replaceFillChild(ln, outline.fill);
12333
+ }
12334
+ function replaceFillChild(parent, fill) {
12335
+ const fillNode = fill.kind === "none" ? { key: "a:noFill", value: {} } : {
12336
+ key: "a:solidFill",
12337
+ value: { "a:srgbClr": { "@_val": fill.color.hex.toUpperCase() } }
12338
+ };
12339
+ const entries = Object.entries(parent).filter(
12340
+ ([key]) => key.startsWith("@_") || !FILL_CHILD_LOCAL_NAMES.has(localName(key))
12341
+ );
12342
+ replaceNodeEntries(parent, entries);
12343
+ insertChildByOrder(
12344
+ parent,
12345
+ fillNode.key,
12346
+ fillNode.value,
12347
+ (name) => [
12348
+ "ln",
12349
+ "effectLst",
12350
+ "effectDag",
12351
+ "scene3d",
12352
+ "sp3d",
12353
+ "extLst",
12354
+ "prstDash",
12355
+ "custDash",
12356
+ "round",
12357
+ "bevel",
12358
+ "miter",
12359
+ "headEnd",
12360
+ "tailEnd"
12361
+ ].includes(name)
12362
+ );
12363
+ }
12364
+ function insertChildByOrder(node, key, value, shouldInsertBefore) {
12365
+ const entries = [];
12366
+ let inserted = false;
12367
+ for (const [entryKey, entryValue] of Object.entries(node)) {
12368
+ if (!inserted && !entryKey.startsWith("@_") && shouldInsertBefore(localName(entryKey))) {
12369
+ entries.push([key, value]);
12370
+ inserted = true;
12371
+ }
12372
+ entries.push([entryKey, entryValue]);
11743
12373
  }
12374
+ if (!inserted) entries.push([key, value]);
12375
+ replaceNodeEntries(node, entries);
11744
12376
  }
11745
12377
  function parseShapeFragmentXml(xml, rootLocalName) {
11746
12378
  const node = getChild(parseXml(xml), rootLocalName);
@@ -11802,12 +12434,104 @@ function booleanOoxmlValue(value) {
11802
12434
  function hasTextRunPropertiesSetValues(properties) {
11803
12435
  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
12436
  }
12437
+ function hasParagraphPropertiesSetValues(properties) {
12438
+ return properties.align !== void 0 || properties.level !== void 0 || properties.bullet !== void 0;
12439
+ }
11805
12440
  function assertTextRunPropertiesEdit(edit) {
11806
12441
  const clear = edit.clear ?? [];
11807
12442
  if (!hasTextRunPropertiesSetValues(edit.set ?? {}) && clear.length === 0) {
11808
12443
  throw new Error("writePptx: text run properties edit must set or clear at least one property");
11809
12444
  }
11810
12445
  }
12446
+ function assertParagraphPropertiesEdit(edit) {
12447
+ const clear = edit.clear ?? [];
12448
+ if (!hasParagraphPropertiesSetValues(edit.set ?? {}) && clear.length === 0) {
12449
+ throw new Error("writePptx: paragraph properties edit must set or clear at least one property");
12450
+ }
12451
+ }
12452
+ function locateParagraphPropertiesForEdit(paragraphs, locator) {
12453
+ let logicalParagraphIndex = 0;
12454
+ for (const paragraph of paragraphs) {
12455
+ const logicalCount = getLogicalParagraphCount(paragraph);
12456
+ if (locator.paragraphIndex >= logicalParagraphIndex && locator.paragraphIndex < logicalParagraphIndex + logicalCount) {
12457
+ if (logicalCount === 1) {
12458
+ return { paragraph, properties: getChild(paragraph, "pPr") };
12459
+ }
12460
+ const relativeIndex = locator.paragraphIndex - logicalParagraphIndex;
12461
+ return {
12462
+ paragraph,
12463
+ properties: getBulletParagraphProperties(paragraph)[relativeIndex]
12464
+ };
12465
+ }
12466
+ logicalParagraphIndex += logicalCount;
12467
+ }
12468
+ return void 0;
12469
+ }
12470
+ function getBulletParagraphProperties(paragraph) {
12471
+ return getChildArray(paragraph, "pPr").filter(
12472
+ (properties) => getChild(properties, "buChar") !== void 0 || getChild(properties, "buAutoNum") !== void 0
12473
+ );
12474
+ }
12475
+ function ensureParagraphProperties(paragraph) {
12476
+ const existing = getChild(paragraph, "pPr");
12477
+ if (existing !== void 0) return existing;
12478
+ const entries = [];
12479
+ let inserted = false;
12480
+ for (const [key, value] of Object.entries(paragraph)) {
12481
+ if (!inserted && !key.startsWith("@_")) {
12482
+ entries.push(["a:pPr", {}]);
12483
+ inserted = true;
12484
+ }
12485
+ entries.push([key, value]);
12486
+ }
12487
+ if (!inserted) entries.push(["a:pPr", {}]);
12488
+ replaceNodeEntries(paragraph, entries);
12489
+ return getChild(paragraph, "pPr") ?? {};
12490
+ }
12491
+ function deleteParagraphProperties(paragraph, pPr) {
12492
+ if (getChild(paragraph, "pPr") === pPr) deleteChild(paragraph, "pPr");
12493
+ }
12494
+ function clearParagraphProperty(pPr, property) {
12495
+ switch (property) {
12496
+ case "align":
12497
+ if (pPr["@_algn"] === void 0) return false;
12498
+ delete pPr["@_algn"];
12499
+ return true;
12500
+ case "level":
12501
+ if (pPr["@_lvl"] === void 0) return false;
12502
+ delete pPr["@_lvl"];
12503
+ return true;
12504
+ case "bullet":
12505
+ return deleteParagraphBullet(pPr);
12506
+ }
12507
+ }
12508
+ function deleteParagraphBullet(pPr) {
12509
+ const deletedNone = deleteChild(pPr, "buNone");
12510
+ const deletedChar = deleteChild(pPr, "buChar");
12511
+ const deletedAutoNum = deleteChild(pPr, "buAutoNum");
12512
+ return deletedNone || deletedChar || deletedAutoNum;
12513
+ }
12514
+ function setParagraphBullet(pPr, bullet) {
12515
+ deleteParagraphBullet(pPr);
12516
+ if (bullet.type === "none") {
12517
+ replaceChild(pPr, "buNone", {});
12518
+ return;
12519
+ }
12520
+ if (bullet.type === "char") {
12521
+ replaceChild(pPr, "buChar", { "@_char": bullet.char });
12522
+ return;
12523
+ }
12524
+ replaceChild(pPr, "buAutoNum", {
12525
+ "@_type": bullet.scheme,
12526
+ "@_startAt": String(bullet.startAt)
12527
+ });
12528
+ }
12529
+ function paragraphAlignOoxmlValue(align) {
12530
+ if (align === "center") return "ctr";
12531
+ if (align === "right") return "r";
12532
+ if (align === "justify") return "just";
12533
+ return "l";
12534
+ }
11811
12535
  function replaceParagraphRunsWithSingleTextRun(paragraph, text) {
11812
12536
  const firstRunProperties = getChild(getFirstRunLikeNode(paragraph), "rPr");
11813
12537
  const replacementRun = {
@@ -11876,6 +12600,8 @@ function validateEdits(edits) {
11876
12600
  const runKeys = /* @__PURE__ */ new Set();
11877
12601
  const paragraphKeys = /* @__PURE__ */ new Set();
11878
12602
  const shapeKeys = /* @__PURE__ */ new Set();
12603
+ const shapeFillKeys = /* @__PURE__ */ new Set();
12604
+ const shapeOutlineKeys = /* @__PURE__ */ new Set();
11879
12605
  const deletedShapeKeys = /* @__PURE__ */ new Set();
11880
12606
  const textRunEdits = [];
11881
12607
  const textRunPropertiesEdits = [];
@@ -11904,6 +12630,8 @@ function validateEdits(edits) {
11904
12630
  case "updateTextRunProperties":
11905
12631
  textRunPropertiesEdits.push(edit);
11906
12632
  break;
12633
+ case "updateParagraphProperties":
12634
+ break;
11907
12635
  case "replaceParagraphPlainText": {
11908
12636
  const key = editHandleNodeKey(edit);
11909
12637
  if (paragraphKeys.has(key)) {
@@ -11924,6 +12652,26 @@ function validateEdits(edits) {
11924
12652
  shapeKeys.add(key);
11925
12653
  break;
11926
12654
  }
12655
+ case "updateShapeFill": {
12656
+ const key = editHandleNodeKey(edit);
12657
+ if (shapeFillKeys.has(key)) {
12658
+ throw new Error(
12659
+ `writePptx: conflicting shape fill edits for handle '${String(edit.handle.nodeId)}'`
12660
+ );
12661
+ }
12662
+ shapeFillKeys.add(key);
12663
+ break;
12664
+ }
12665
+ case "updateShapeOutline": {
12666
+ const key = editHandleNodeKey(edit);
12667
+ if (shapeOutlineKeys.has(key)) {
12668
+ throw new Error(
12669
+ `writePptx: conflicting shape outline edits for handle '${String(edit.handle.nodeId)}'`
12670
+ );
12671
+ }
12672
+ shapeOutlineKeys.add(key);
12673
+ break;
12674
+ }
11927
12675
  case "deleteShape": {
11928
12676
  const key = editHandleNodeKey(edit);
11929
12677
  if (deletedShapeKeys.has(key)) {
@@ -11937,6 +12685,7 @@ function validateEdits(edits) {
11937
12685
  case "replaceImage":
11938
12686
  case "addEmptySlideFromLayout":
11939
12687
  case "duplicateSlide":
12688
+ case "moveSlide":
11940
12689
  case "deleteSlide":
11941
12690
  break;
11942
12691
  }
@@ -12043,6 +12792,9 @@ function serializePresentationWithSlideTopologyEdits(source, operations) {
12043
12792
  case "removeSlide":
12044
12793
  removeSlideId(sldIdLst, operation.relationshipId);
12045
12794
  break;
12795
+ case "moveSlide":
12796
+ moveSlideId(sldIdLst, operation.relationshipId, operation.toIndex);
12797
+ break;
12046
12798
  }
12047
12799
  }
12048
12800
  return encodeXml(XML_DECLARATION + xmlBuilder2.build(stripXmlProcessingInstruction(root)));
@@ -12085,6 +12837,24 @@ function removeSlideId(sldIdLst, relationshipId) {
12085
12837
  const { key, items } = slideIdEntries(sldIdLst);
12086
12838
  sldIdLst[key] = items.filter((item) => getRelationshipAttr(item) !== relationshipId);
12087
12839
  }
12840
+ function moveSlideId(sldIdLst, relationshipId, toIndex) {
12841
+ const { key, items } = slideIdEntries(sldIdLst);
12842
+ const fromIndex = items.findIndex((item2) => getRelationshipAttr(item2) === relationshipId);
12843
+ if (fromIndex === -1) {
12844
+ throw new Error(
12845
+ `writePptx: slide relationship '${relationshipId}' was not found in p:sldIdLst`
12846
+ );
12847
+ }
12848
+ if (toIndex < 0 || toIndex >= items.length) {
12849
+ throw new Error(`writePptx: slide move target index '${toIndex}' is out of range`);
12850
+ }
12851
+ if (fromIndex === toIndex) return;
12852
+ const moved = [...items];
12853
+ const [item] = moved.splice(fromIndex, 1);
12854
+ if (item === void 0) return;
12855
+ moved.splice(toIndex, 0, item);
12856
+ sldIdLst[key] = moved;
12857
+ }
12088
12858
  function slideIdEntries(sldIdLst) {
12089
12859
  const key = namespacedChildKey(sldIdLst, "p:sldId", "sldId");
12090
12860
  const value = sldIdLst[key];
@@ -12098,7 +12868,7 @@ function getRelationshipAttr(node) {
12098
12868
  return getNamespacedAttr(node, "id");
12099
12869
  }
12100
12870
  var CONTENT_TYPES_PART2 = "[Content_Types].xml";
12101
- var RELS_CONTENT_TYPE2 = "application/vnd.openxmlformats-package.relationships+xml";
12871
+ var RELS_CONTENT_TYPE3 = "application/vnd.openxmlformats-package.relationships+xml";
12102
12872
  function writePptx(source) {
12103
12873
  const edits = source.edits ?? [];
12104
12874
  validateEdits(edits);
@@ -12145,7 +12915,7 @@ function writePptx(source) {
12145
12915
  }
12146
12916
  for (const part of source.packageGraph.parts) {
12147
12917
  if (written.has(part.partPath)) continue;
12148
- if (part.contentType === RELS_CONTENT_TYPE2 || isRelationshipPart(part.partPath)) continue;
12918
+ if (part.contentType === RELS_CONTENT_TYPE3 || isRelationshipPart(part.partPath)) continue;
12149
12919
  throw new Error(
12150
12920
  "writePptx: no preserved package material for part '" + part.partPath + "'; edited part generation is not implemented in the no-edit writer"
12151
12921
  );
@@ -15498,6 +16268,23 @@ var pptxTextBodySchema = new Schema({
15498
16268
  }
15499
16269
  }
15500
16270
  });
16271
+ var EDITABLE_PARAGRAPH_PROPERTIES2 = [
16272
+ "align",
16273
+ "level",
16274
+ "bullet"
16275
+ ];
16276
+ var AUTO_NUM_SCHEMES2 = [
16277
+ "arabicPeriod",
16278
+ "arabicParenR",
16279
+ "romanUcPeriod",
16280
+ "romanLcPeriod",
16281
+ "alphaUcPeriod",
16282
+ "alphaLcPeriod",
16283
+ "alphaLcParenR",
16284
+ "alphaUcParenR",
16285
+ "arabicPlain"
16286
+ ];
16287
+ var AUTO_NUM_SCHEME_SET = new Set(AUTO_NUM_SCHEMES2);
15501
16288
  function textBodyToProseMirrorDocJson(textBody) {
15502
16289
  assertSupportedTextBody(textBody);
15503
16290
  return {
@@ -15563,8 +16350,12 @@ function proseMirrorDocJsonToEditorCommands(originalTextBody, docJson) {
15563
16350
  (editedParagraph, paragraphIndex) => {
15564
16351
  const originalParagraph = originalTextBody.paragraphs[paragraphIndex];
15565
16352
  if (originalParagraph === void 0) return [];
16353
+ const paragraphPropertyCommands = paragraphPropertiesToEditorCommands(
16354
+ originalParagraph,
16355
+ editedParagraph
16356
+ );
15566
16357
  if (paragraphRunHandlesMatch(originalParagraph, editedParagraph)) {
15567
- return editedParagraph.runs.flatMap(
16358
+ const textRunCommands = editedParagraph.runs.flatMap(
15568
16359
  (editedRun, runIndex) => {
15569
16360
  const originalRun = originalParagraph.runs[runIndex];
15570
16361
  if (originalRun === void 0 || editedRun.text === originalRun.text) return [];
@@ -15582,6 +16373,7 @@ function proseMirrorDocJsonToEditorCommands(originalTextBody, docJson) {
15582
16373
  ];
15583
16374
  }
15584
16375
  );
16376
+ return [...paragraphPropertyCommands, ...textRunCommands];
15585
16377
  }
15586
16378
  if (editedParagraph.handle === void 0) {
15587
16379
  throw new Error(
@@ -15589,6 +16381,7 @@ function proseMirrorDocJsonToEditorCommands(originalTextBody, docJson) {
15589
16381
  );
15590
16382
  }
15591
16383
  return [
16384
+ ...paragraphPropertyCommands,
15592
16385
  {
15593
16386
  kind: "replaceParagraphPlainText",
15594
16387
  handle: editedParagraph.handle,
@@ -15604,10 +16397,14 @@ function validateProseMirrorDocJson(docJson) {
15604
16397
  }
15605
16398
  function paragraphJsonToSourceParagraph(originalParagraph, paragraphJson) {
15606
16399
  const groups = collectRunGroups(paragraphJson, originalParagraph);
16400
+ const properties = paragraphPropertiesFromJson(
16401
+ originalParagraph?.properties,
16402
+ paragraphJson.attrs?.properties
16403
+ );
15607
16404
  return {
15608
- ...originalParagraph ?? {},
15609
16405
  runs: groups.map(sourceRunFromGroup),
15610
- ...originalParagraph?.properties !== void 0 ? { properties: originalParagraph.properties } : {},
16406
+ ...originalParagraph?.rawSidecars !== void 0 ? { rawSidecars: originalParagraph.rawSidecars } : {},
16407
+ ...properties !== void 0 ? { properties } : {},
15611
16408
  ...originalParagraph?.handle !== void 0 ? { handle: originalParagraph.handle } : sourceHandleFromUnknown(paragraphJson.attrs?.handle) !== void 0 ? { handle: sourceHandleFromUnknown(paragraphJson.attrs?.handle) } : {}
15612
16409
  };
15613
16410
  }
@@ -15657,6 +16454,107 @@ function paragraphRunHandlesMatch(originalParagraph, editedParagraph) {
15657
16454
  function paragraphPlainText(paragraph) {
15658
16455
  return paragraph.runs.map((run) => run.text).join("");
15659
16456
  }
16457
+ function paragraphPropertiesToEditorCommands(originalParagraph, editedParagraph) {
16458
+ if (editedParagraph.handle === void 0) {
16459
+ if (paragraphEditablePropertiesEqual(originalParagraph.properties, editedParagraph.properties)) {
16460
+ return [];
16461
+ }
16462
+ throw new Error("proseMirrorDocJsonToEditorCommands: changed paragraph has no source handle");
16463
+ }
16464
+ const set = {};
16465
+ const clear = [];
16466
+ if (!stableValueEqual3(originalParagraph.properties?.align, editedParagraph.properties?.align)) {
16467
+ if (editedParagraph.properties?.align === void 0) clear.push("align");
16468
+ else set.align = editedParagraph.properties.align;
16469
+ }
16470
+ if (!stableValueEqual3(originalParagraph.properties?.level, editedParagraph.properties?.level)) {
16471
+ if (editedParagraph.properties?.level === void 0) clear.push("level");
16472
+ else set.level = editedParagraph.properties.level;
16473
+ }
16474
+ if (!stableValueEqual3(originalParagraph.properties?.bullet, editedParagraph.properties?.bullet)) {
16475
+ if (editedParagraph.properties?.bullet === void 0) clear.push("bullet");
16476
+ else set.bullet = editedParagraph.properties.bullet;
16477
+ }
16478
+ const commands = [];
16479
+ if (clear.length > 0) {
16480
+ commands.push({
16481
+ kind: "clearParagraphProperties",
16482
+ handle: editedParagraph.handle,
16483
+ properties: clear
16484
+ });
16485
+ }
16486
+ if (Object.keys(set).length > 0) {
16487
+ commands.push({
16488
+ kind: "setParagraphProperties",
16489
+ handle: editedParagraph.handle,
16490
+ properties: set
16491
+ });
16492
+ }
16493
+ return commands;
16494
+ }
16495
+ function paragraphPropertiesFromJson(originalProperties, value) {
16496
+ if (!isRecord(value)) return originalProperties;
16497
+ const next = { ...originalProperties ?? {} };
16498
+ for (const property of EDITABLE_PARAGRAPH_PROPERTIES2) {
16499
+ if (!Object.prototype.hasOwnProperty.call(value, property)) {
16500
+ delete next[property];
16501
+ continue;
16502
+ }
16503
+ const propertyValue = value[property];
16504
+ if (propertyValue === null || propertyValue === void 0) {
16505
+ delete next[property];
16506
+ } else if (property === "align") {
16507
+ next.align = paragraphAlignFromUnknown(propertyValue);
16508
+ } else if (property === "level") {
16509
+ next.level = paragraphLevelFromUnknown(propertyValue);
16510
+ } else {
16511
+ next.bullet = paragraphBulletFromUnknown(propertyValue);
16512
+ }
16513
+ }
16514
+ return Object.keys(next).length > 0 ? next : void 0;
16515
+ }
16516
+ function paragraphAlignFromUnknown(value) {
16517
+ if (value === "left" || value === "center" || value === "right" || value === "justify") {
16518
+ return value;
16519
+ }
16520
+ throw new Error("ProseMirror paragraph properties align must be left, center, right, or justify");
16521
+ }
16522
+ function paragraphLevelFromUnknown(value) {
16523
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 8) {
16524
+ return value;
16525
+ }
16526
+ throw new Error("ProseMirror paragraph properties level must be an integer from 0 to 8");
16527
+ }
16528
+ function paragraphBulletFromUnknown(value) {
16529
+ if (!isRecord(value) || typeof value.type !== "string") {
16530
+ throw new Error("ProseMirror paragraph properties bullet must be an object");
16531
+ }
16532
+ if (value.type === "none") return { type: "none" };
16533
+ if (value.type === "char") {
16534
+ if (typeof value.char !== "string" || value.char.length === 0) {
16535
+ throw new Error("ProseMirror paragraph properties bullet.char must be a non-empty string");
16536
+ }
16537
+ return { type: "char", char: value.char };
16538
+ }
16539
+ if (value.type === "autoNum") {
16540
+ if (!isAutoNumScheme(value.scheme)) {
16541
+ throw new Error("ProseMirror paragraph properties bullet.scheme is unsupported");
16542
+ }
16543
+ if (typeof value.startAt !== "number" || !Number.isInteger(value.startAt) || value.startAt < 1) {
16544
+ throw new Error("ProseMirror paragraph properties bullet.startAt must be positive");
16545
+ }
16546
+ return { type: "autoNum", scheme: value.scheme, startAt: value.startAt };
16547
+ }
16548
+ throw new Error("ProseMirror paragraph properties bullet.type is unsupported");
16549
+ }
16550
+ function isAutoNumScheme(value) {
16551
+ return typeof value === "string" && AUTO_NUM_SCHEME_SET.has(value);
16552
+ }
16553
+ function paragraphEditablePropertiesEqual(left, right) {
16554
+ return EDITABLE_PARAGRAPH_PROPERTIES2.every(
16555
+ (property) => stableValueEqual3(left?.[property], right?.[property])
16556
+ );
16557
+ }
15660
16558
  function parsePptxTextBodyProseMirrorDocJson(value) {
15661
16559
  if (!isRecord(value) || value.type !== "doc") {
15662
16560
  throw new Error("ProseMirror text body doc JSON must be a doc node");
@@ -15664,7 +16562,7 @@ function parsePptxTextBodyProseMirrorDocJson(value) {
15664
16562
  if (value.content !== void 0 && readArray(value.content, isParagraphJson) === void 0) {
15665
16563
  throw new Error("ProseMirror text body doc JSON content must contain paragraph nodes");
15666
16564
  }
15667
- const content = readArray(value.content, isParagraphJson);
16565
+ const content = readArray(value.content, isParagraphJson)?.map(normalizeParagraphJson);
15668
16566
  return {
15669
16567
  type: "doc",
15670
16568
  ...content !== void 0 ? { content } : {}
@@ -15677,6 +16575,13 @@ function isParagraphJson(value) {
15677
16575
  }
15678
16576
  return value.attrs === void 0 || isRecord(value.attrs);
15679
16577
  }
16578
+ function normalizeParagraphJson(value) {
16579
+ return {
16580
+ type: "paragraph",
16581
+ ...value.attrs !== void 0 ? { attrs: value.attrs } : {},
16582
+ ...value.content !== void 0 ? { content: value.content } : {}
16583
+ };
16584
+ }
15680
16585
  function isTextJson(value) {
15681
16586
  if (!isRecord(value) || value.type !== "text" || typeof value.text !== "string") return false;
15682
16587
  if (value.marks === void 0) return true;
@@ -15714,6 +16619,22 @@ function sourceHandleKey(handle) {
15714
16619
  handle.orderingSlot ?? ""
15715
16620
  ].join("\0");
15716
16621
  }
16622
+ function stableValueEqual3(left, right) {
16623
+ if (Object.is(left, right)) return true;
16624
+ if (Array.isArray(left) || Array.isArray(right)) {
16625
+ if (!Array.isArray(left) || !Array.isArray(right)) return false;
16626
+ if (left.length !== right.length) return false;
16627
+ return left.every((value, index) => stableValueEqual3(value, right[index]));
16628
+ }
16629
+ if (isRecord(left) || isRecord(right)) {
16630
+ if (!isRecord(left) || !isRecord(right)) return false;
16631
+ const leftKeys = Object.keys(left).sort();
16632
+ const rightKeys = Object.keys(right).sort();
16633
+ if (!stableValueEqual3(leftKeys, rightKeys)) return false;
16634
+ return leftKeys.every((key) => stableValueEqual3(left[key], right[key]));
16635
+ }
16636
+ return false;
16637
+ }
15717
16638
  function isRecord(value) {
15718
16639
  return typeof value === "object" && value !== null && !Array.isArray(value);
15719
16640
  }
@@ -15827,12 +16748,20 @@ function applyCommandToDocument(document, command) {
15827
16748
  return setTextRunPropertiesCommand(document, command);
15828
16749
  case "clearTextRunProperties":
15829
16750
  return clearTextRunPropertiesCommand(document, command);
16751
+ case "setParagraphProperties":
16752
+ return setParagraphPropertiesCommand(document, command);
16753
+ case "clearParagraphProperties":
16754
+ return clearParagraphPropertiesCommand(document, command);
15830
16755
  case "moveShape":
15831
16756
  return moveShape(document, command);
15832
16757
  case "resizeShape":
15833
16758
  return resizeShape(document, command);
15834
16759
  case "setShapeTransform":
15835
16760
  return setShapeTransform(document, command);
16761
+ case "setShapeFill":
16762
+ return setShapeFillCommand(document, command);
16763
+ case "setShapeOutline":
16764
+ return setShapeOutlineCommand(document, command);
15836
16765
  case "addTextBox":
15837
16766
  return addTextBoxCommand(document, command);
15838
16767
  case "addConnector":
@@ -15845,6 +16774,8 @@ function applyCommandToDocument(document, command) {
15845
16774
  return addEmptySlideFromLayout(document, command);
15846
16775
  case "duplicateSlide":
15847
16776
  return duplicateSlide(document, command.handle);
16777
+ case "moveSlide":
16778
+ return moveSlide(document, command.handle, command);
15848
16779
  case "deleteSlide":
15849
16780
  return deleteSlide(document, command.handle);
15850
16781
  }
@@ -15917,6 +16848,22 @@ function clearTextRunPropertiesCommand(document, command) {
15917
16848
  }
15918
16849
  return clearTextRunProperties(document, command.handle, command.properties);
15919
16850
  }
16851
+ function setParagraphPropertiesCommand(document, command) {
16852
+ requireNonEmptyParagraphPropertySet(command.properties, "setParagraphProperties");
16853
+ validateParagraphPropertySet(command.properties, "setParagraphProperties");
16854
+ return setParagraphProperties(document, command.handle, command.properties);
16855
+ }
16856
+ function clearParagraphPropertiesCommand(document, command) {
16857
+ if (command.properties.length === 0) {
16858
+ throw new Error("clearParagraphProperties: properties must contain at least one property name");
16859
+ }
16860
+ for (const property of command.properties) {
16861
+ if (!EDITABLE_PARAGRAPH_PROPERTY_SET2.has(property)) {
16862
+ throw new Error(`clearParagraphProperties: unsupported paragraph property '${property}'`);
16863
+ }
16864
+ }
16865
+ return clearParagraphProperties(document, command.handle, command.properties);
16866
+ }
15920
16867
  function moveShape(document, command) {
15921
16868
  requireFiniteEmu(command.offsetX, "moveShape", "offsetX");
15922
16869
  requireFiniteEmu(command.offsetY, "moveShape", "offsetY");
@@ -15952,6 +16899,14 @@ function setShapeTransform(document, command) {
15952
16899
  height: command.height
15953
16900
  });
15954
16901
  }
16902
+ function setShapeFillCommand(document, command) {
16903
+ validateShapeFill(command.fill, "setShapeFill");
16904
+ return setShapeFill(document, command.handle, command.fill);
16905
+ }
16906
+ function setShapeOutlineCommand(document, command) {
16907
+ validateShapeOutline(command.outline, "setShapeOutline");
16908
+ return setShapeOutline(document, command.handle, command.outline);
16909
+ }
15955
16910
  var EDITABLE_TEXT_RUN_PROPERTIES2 = [
15956
16911
  "bold",
15957
16912
  "italic",
@@ -15961,6 +16916,24 @@ var EDITABLE_TEXT_RUN_PROPERTIES2 = [
15961
16916
  "typeface"
15962
16917
  ];
15963
16918
  var EDITABLE_TEXT_RUN_PROPERTY_SET2 = new Set(EDITABLE_TEXT_RUN_PROPERTIES2);
16919
+ var EDITABLE_PARAGRAPH_PROPERTIES22 = [
16920
+ "align",
16921
+ "level",
16922
+ "bullet"
16923
+ ];
16924
+ var EDITABLE_PARAGRAPH_PROPERTY_SET2 = new Set(EDITABLE_PARAGRAPH_PROPERTIES22);
16925
+ var PARAGRAPH_ALIGN_VALUES = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
16926
+ var AUTO_NUM_SCHEMES22 = /* @__PURE__ */ new Set([
16927
+ "arabicPeriod",
16928
+ "arabicParenR",
16929
+ "romanUcPeriod",
16930
+ "romanLcPeriod",
16931
+ "alphaUcPeriod",
16932
+ "alphaLcPeriod",
16933
+ "alphaLcParenR",
16934
+ "alphaUcParenR",
16935
+ "arabicPlain"
16936
+ ]);
15964
16937
  function requireNonEmptyPropertySet(properties, commandName) {
15965
16938
  if (Object.values(properties).every((value) => value === void 0)) {
15966
16939
  throw new Error(`${commandName}: properties must contain at least one defined property`);
@@ -15990,11 +16963,72 @@ function validateTextRunPropertySet(properties, commandName) {
15990
16963
  }
15991
16964
  }
15992
16965
  }
16966
+ function requireNonEmptyParagraphPropertySet(properties, commandName) {
16967
+ if (Object.values(properties).every((value) => value === void 0)) {
16968
+ throw new Error(`${commandName}: properties must contain at least one defined property`);
16969
+ }
16970
+ }
16971
+ function validateParagraphPropertySet(properties, commandName) {
16972
+ for (const property of Object.keys(properties)) {
16973
+ if (!EDITABLE_PARAGRAPH_PROPERTY_SET2.has(property)) {
16974
+ throw new Error(`${commandName}: unsupported paragraph property '${property}'`);
16975
+ }
16976
+ }
16977
+ if (properties.align !== void 0 && !PARAGRAPH_ALIGN_VALUES.has(properties.align)) {
16978
+ throw new Error(`${commandName}: align must be left, center, right, or justify`);
16979
+ }
16980
+ if (properties.level !== void 0 && (!Number.isInteger(properties.level) || properties.level < 0 || properties.level > 8)) {
16981
+ throw new Error(`${commandName}: level must be an integer from 0 to 8`);
16982
+ }
16983
+ if (properties.bullet !== void 0) {
16984
+ validateParagraphBullet2(properties.bullet, commandName);
16985
+ }
16986
+ }
16987
+ function validateParagraphBullet2(bullet, commandName) {
16988
+ if (bullet.type === "none") return;
16989
+ if (bullet.type === "char") {
16990
+ if (bullet.char.length === 0) {
16991
+ throw new Error(`${commandName}: bullet.char must be a non-empty string`);
16992
+ }
16993
+ return;
16994
+ }
16995
+ if (bullet.type === "autoNum") {
16996
+ if (!AUTO_NUM_SCHEMES22.has(bullet.scheme)) {
16997
+ throw new Error(`${commandName}: unsupported bullet auto-numbering scheme`);
16998
+ }
16999
+ if (!Number.isInteger(bullet.startAt) || bullet.startAt < 1) {
17000
+ throw new Error(`${commandName}: bullet.startAt must be a positive integer`);
17001
+ }
17002
+ return;
17003
+ }
17004
+ throw new Error(`${commandName}: unsupported bullet type`);
17005
+ }
15993
17006
  function requireBooleanOrUndefined2(value, commandName, fieldName) {
15994
17007
  if (value !== void 0 && typeof value !== "boolean") {
15995
17008
  throw new Error(`${commandName}: ${fieldName} must be a boolean value`);
15996
17009
  }
15997
17010
  }
17011
+ function validateShapeOutline(outline, commandName) {
17012
+ if (outline.width === void 0 && outline.fill === void 0) {
17013
+ throw new Error(`${commandName}: outline must set width or fill`);
17014
+ }
17015
+ if (outline.width !== void 0) {
17016
+ requirePositiveFiniteEmu(outline.width, commandName, "width");
17017
+ }
17018
+ if (outline.fill !== void 0) validateShapeFill(outline.fill, commandName);
17019
+ }
17020
+ function validateShapeFill(fill, commandName) {
17021
+ if (fill.kind === "none") return;
17022
+ if (fill.kind !== "solid") {
17023
+ throw new Error(`${commandName}: only solid and none fills are supported`);
17024
+ }
17025
+ if (fill.color.kind !== "srgb") {
17026
+ throw new Error(`${commandName}: only srgb solid fill colors are supported`);
17027
+ }
17028
+ if (!/^[0-9A-Fa-f]{6}$/.test(fill.color.hex)) {
17029
+ throw new Error(`${commandName}: color.hex must be a 6-digit hex value`);
17030
+ }
17031
+ }
15998
17032
  function requireEditableShapeTransform(document, handle, commandName) {
15999
17033
  const shape = findShapeNodeBySourceHandle(document, handle);
16000
17034
  if (shape === void 0) {
@@ -16023,8 +17057,12 @@ function normalizeEditorEdits(document) {
16023
17057
  if (edits === void 0) return document;
16024
17058
  const seenTextRuns = /* @__PURE__ */ new Set();
16025
17059
  const seenTextRunProperties = /* @__PURE__ */ new Map();
17060
+ const seenParagraphProperties = /* @__PURE__ */ new Map();
16026
17061
  const seenParagraphs = /* @__PURE__ */ new Set();
16027
17062
  const seenShapeTransforms = /* @__PURE__ */ new Set();
17063
+ const seenShapeFills = /* @__PURE__ */ new Set();
17064
+ const seenShapeOutlineProperties = /* @__PURE__ */ new Map();
17065
+ const normalizedShapeOutlineEdits = /* @__PURE__ */ new Map();
16028
17066
  const normalizedReversed = [];
16029
17067
  let changed = false;
16030
17068
  for (let index = edits.length - 1; index >= 0; index -= 1) {
@@ -16057,6 +17095,16 @@ function normalizeEditorEdits(document) {
16057
17095
  normalizedReversed.push(normalized);
16058
17096
  continue;
16059
17097
  }
17098
+ if (edit.kind === "updateParagraphProperties") {
17099
+ const normalized = normalizeParagraphPropertiesEdit(edit, seenParagraphProperties);
17100
+ if (normalized === void 0) {
17101
+ changed = true;
17102
+ continue;
17103
+ }
17104
+ if (!editorEditsEqual(normalized, edit)) changed = true;
17105
+ normalizedReversed.push(normalized);
17106
+ continue;
17107
+ }
16060
17108
  if (edit.kind === "replaceParagraphPlainText") {
16061
17109
  const key = editHandleNodeKey2(edit);
16062
17110
  if (seenParagraphs.has(key)) {
@@ -16073,6 +17121,32 @@ function normalizeEditorEdits(document) {
16073
17121
  }
16074
17122
  seenShapeTransforms.add(key);
16075
17123
  }
17124
+ if (edit.kind === "updateShapeFill") {
17125
+ const key = editHandleNodeKey2(edit);
17126
+ if (seenShapeFills.has(key)) {
17127
+ changed = true;
17128
+ continue;
17129
+ }
17130
+ seenShapeFills.add(key);
17131
+ }
17132
+ if (edit.kind === "updateShapeOutline") {
17133
+ const normalized = normalizeShapeOutlineEdit(
17134
+ edit,
17135
+ seenShapeOutlineProperties,
17136
+ normalizedShapeOutlineEdits
17137
+ );
17138
+ if (normalized === void 0) {
17139
+ changed = true;
17140
+ continue;
17141
+ }
17142
+ if (normalized.merged) {
17143
+ changed = true;
17144
+ continue;
17145
+ }
17146
+ if (!editorEditsEqual(normalized.edit, edit)) changed = true;
17147
+ normalizedReversed.push(normalized.edit);
17148
+ continue;
17149
+ }
16076
17150
  normalizedReversed.push(edit);
16077
17151
  }
16078
17152
  if (!changed && normalizedReversed.length === edits.length) return document;
@@ -16149,6 +17223,67 @@ function normalizeTextRunPropertiesEdit(edit, seenTextRunProperties) {
16149
17223
  };
16150
17224
  return normalized;
16151
17225
  }
17226
+ function normalizeParagraphPropertiesEdit(edit, seenParagraphProperties) {
17227
+ const key = editHandleNodeKey2(edit);
17228
+ let seenProperties = seenParagraphProperties.get(key);
17229
+ if (seenProperties === void 0) {
17230
+ seenProperties = /* @__PURE__ */ new Set();
17231
+ seenParagraphProperties.set(key, seenProperties);
17232
+ }
17233
+ const set = {};
17234
+ if (edit.set?.align !== void 0 && !seenProperties.has("align")) {
17235
+ seenProperties.add("align");
17236
+ set.align = edit.set.align;
17237
+ }
17238
+ if (edit.set?.level !== void 0 && !seenProperties.has("level")) {
17239
+ seenProperties.add("level");
17240
+ set.level = edit.set.level;
17241
+ }
17242
+ if (edit.set?.bullet !== void 0 && !seenProperties.has("bullet")) {
17243
+ seenProperties.add("bullet");
17244
+ set.bullet = edit.set.bullet;
17245
+ }
17246
+ const clear = (edit.clear ?? []).filter((property) => !seenProperties.has(property));
17247
+ for (const property of clear) seenProperties.add(property);
17248
+ if (clear.length === 0 && Object.keys(set).length === 0) return void 0;
17249
+ const normalized = {
17250
+ kind: "updateParagraphProperties",
17251
+ handle: edit.handle,
17252
+ ...Object.keys(set).length > 0 ? { set } : {},
17253
+ ...clear.length > 0 ? { clear } : {}
17254
+ };
17255
+ return normalized;
17256
+ }
17257
+ function normalizeShapeOutlineEdit(edit, seenShapeOutlineProperties, normalizedShapeOutlineEdits) {
17258
+ const key = editHandleNodeKey2(edit);
17259
+ let seenProperties = seenShapeOutlineProperties.get(key);
17260
+ if (seenProperties === void 0) {
17261
+ seenProperties = /* @__PURE__ */ new Set();
17262
+ seenShapeOutlineProperties.set(key, seenProperties);
17263
+ }
17264
+ const outline = {};
17265
+ if (edit.outline.width !== void 0 && !seenProperties.has("width")) {
17266
+ seenProperties.add("width");
17267
+ outline.width = edit.outline.width;
17268
+ }
17269
+ if (edit.outline.fill !== void 0 && !seenProperties.has("fill")) {
17270
+ seenProperties.add("fill");
17271
+ outline.fill = edit.outline.fill;
17272
+ }
17273
+ if (Object.keys(outline).length === 0) return void 0;
17274
+ const existing = normalizedShapeOutlineEdits.get(key);
17275
+ if (existing !== void 0) {
17276
+ existing.outline = { ...outline, ...existing.outline };
17277
+ return { merged: true };
17278
+ }
17279
+ const normalized = {
17280
+ kind: "updateShapeOutline",
17281
+ handle: edit.handle,
17282
+ outline
17283
+ };
17284
+ normalizedShapeOutlineEdits.set(key, normalized);
17285
+ return { edit: normalized, merged: false };
17286
+ }
16152
17287
 
16153
17288
  // src/browser-editor.ts
16154
17289
  var EMU_PER_INCH2 = 914400;
@@ -16161,6 +17296,12 @@ var DEFAULT_TEXT_BOX_BOUNDS_PX = {
16161
17296
  height: 72
16162
17297
  };
16163
17298
  var DEFAULT_TEXT_BOX_TEXT = "New text box";
17299
+ var DEFAULT_CONNECTOR_BOUNDS_PX = {
17300
+ x: 144,
17301
+ y: 144,
17302
+ width: 288,
17303
+ height: 96
17304
+ };
16164
17305
  var IMAGE_REL_TYPE3 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
16165
17306
  var IMAGE_ACCEPT_BY_CONTENT_TYPE = {
16166
17307
  "image/png": "image/png,.png",
@@ -16258,6 +17399,32 @@ var BrowserPptxEditorSession = class _BrowserPptxEditorSession {
16258
17399
  await this.renderCurrentSlides();
16259
17400
  return this.response(result.warnings);
16260
17401
  }
17402
+ async addConnector(slideNumber = 1, options = {}) {
17403
+ const slide = this.#session.document.slides[slideNumber - 1];
17404
+ if (slide?.handle === void 0) {
17405
+ throw new Error("addConnector: slide handle was not found in PptxSourceModel source");
17406
+ }
17407
+ const existingShapeKeys = new Set(slide.shapes.map(shapeSourceKey));
17408
+ const result = this.#session.apply({
17409
+ kind: "addConnector",
17410
+ slideHandle: slide.handle,
17411
+ preset: "straightConnector1",
17412
+ offsetX: pxToEmu(options.x ?? DEFAULT_CONNECTOR_BOUNDS_PX.x),
17413
+ offsetY: pxToEmu(options.y ?? DEFAULT_CONNECTOR_BOUNDS_PX.y),
17414
+ width: pxToEmu(options.width ?? DEFAULT_CONNECTOR_BOUNDS_PX.width),
17415
+ height: pxToEmu(options.height ?? DEFAULT_CONNECTOR_BOUNDS_PX.height),
17416
+ outline: {
17417
+ tailEnd: { type: "triangle", width: "med", length: "med" }
17418
+ },
17419
+ ...options.name !== void 0 ? { name: options.name } : {}
17420
+ });
17421
+ if (!result.ok) {
17422
+ throw new Error(result.message);
17423
+ }
17424
+ this.#selectNewShape(slideNumber, existingShapeKeys);
17425
+ await this.renderCurrentSlides();
17426
+ return this.response(result.warnings);
17427
+ }
16261
17428
  async deleteShape(handle) {
16262
17429
  const result = this.#session.apply({ kind: "deleteShape", handle });
16263
17430
  if (!result.ok) {
@@ -16370,10 +17537,10 @@ function isEditableTransformShape(shape) {
16370
17537
  return !shape.rawSidecars?.some((sidecar) => sidecar.node.name === "mc:AlternateContent");
16371
17538
  }
16372
17539
  function isDeletableShape(shape, slideShapes) {
16373
- if (shape.kind !== "shape" || shape.handle?.nodeId === void 0) {
17540
+ if (shape.kind !== "shape" && shape.kind !== "connector" || shape.handle?.nodeId === void 0) {
16374
17541
  return false;
16375
17542
  }
16376
- if (isShapeReferencedByConnector(shape, slideShapes)) {
17543
+ if (shape.kind === "shape" && isShapeReferencedByConnector(shape, slideShapes)) {
16377
17544
  return false;
16378
17545
  }
16379
17546
  return !shape.rawSidecars?.some((sidecar) => sidecar.node.name === "mc:AlternateContent");