@shbernal/ts-pptx 3.4.0 → 3.6.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.
Files changed (66) hide show
  1. package/dist/{autopage-BYlTzX-l.js → autopage-DkjYZUFD.js} +3 -3
  2. package/dist/{autopage-BYlTzX-l.js.map → autopage-DkjYZUFD.js.map} +1 -1
  3. package/dist/browser.d.ts +5 -5
  4. package/dist/browser.js +4 -4
  5. package/dist/browser.js.map +1 -1
  6. package/dist/{diagnostics-B0OmTOoz.d.ts → diagnostics-Du_s75MT.d.ts} +20 -5
  7. package/dist/diagnostics-Du_s75MT.d.ts.map +1 -0
  8. package/dist/{errors-fB1KDJKQ.d.ts → errors-PflMPWBn.d.ts} +3 -3
  9. package/dist/{errors-fB1KDJKQ.d.ts.map → errors-PflMPWBn.d.ts.map} +1 -1
  10. package/dist/{fit-TjpNbMru.js → fit-BKs0zxMI.js} +3 -3
  11. package/dist/{fit-TjpNbMru.js.map → fit-BKs0zxMI.js.map} +1 -1
  12. package/dist/{html-dom-yUSdCExA.js → html-dom-B0I0QSbU.js} +4 -4
  13. package/dist/{html-dom-yUSdCExA.js.map → html-dom-B0I0QSbU.js.map} +1 -1
  14. package/dist/html.d.ts +3 -3
  15. package/dist/html.js +1 -1
  16. package/dist/index.d.ts +5 -5
  17. package/dist/index.js +3 -3
  18. package/dist/inspect.d.ts +28 -4
  19. package/dist/inspect.d.ts.map +1 -1
  20. package/dist/inspect.js +42 -11
  21. package/dist/inspect.js.map +1 -1
  22. package/dist/math.d.ts +1 -1
  23. package/dist/measure.d.ts +2 -2
  24. package/dist/measure.js +1 -1
  25. package/dist/{namespaces-Sy_m7Nhd.js → namespaces-BVnNu7sg.js} +77 -3
  26. package/dist/namespaces-BVnNu7sg.js.map +1 -0
  27. package/dist/node.d.ts +5 -5
  28. package/dist/node.js +3 -3
  29. package/dist/{notes-bWApshah.js → notes-RLCwdlTE.js} +289 -313
  30. package/dist/{notes-bWApshah.js.map → notes-RLCwdlTE.js.map} +1 -1
  31. package/dist/{pres-Dybf7ZUl.d.ts → pres-C5__xkDh.d.ts} +2 -2
  32. package/dist/{pres-Dybf7ZUl.d.ts.map → pres-C5__xkDh.d.ts.map} +1 -1
  33. package/dist/{presentation-3rAKKRfm.js → presentation-Cp7vzlAw.js} +566 -519
  34. package/dist/presentation-Cp7vzlAw.js.map +1 -0
  35. package/dist/{presentation-types-D7cgT-eG.d.ts → presentation-types-DP_8jog7.d.ts} +514 -276
  36. package/dist/presentation-types-DP_8jog7.d.ts.map +1 -0
  37. package/dist/read.d.ts +31 -48
  38. package/dist/read.d.ts.map +1 -1
  39. package/dist/read.js +3 -3
  40. package/dist/script.d.ts +2 -2
  41. package/dist/script.d.ts.map +1 -1
  42. package/dist/script.js +84 -75
  43. package/dist/script.js.map +1 -1
  44. package/dist/{shapes-Dyw5bxwO.js → shapes-BemroU-z.js} +1010 -306
  45. package/dist/shapes-BemroU-z.js.map +1 -0
  46. package/dist/{slide-B_aInw3N.d.ts → slide-js0QpRV-.d.ts} +1278 -1254
  47. package/dist/slide-js0QpRV-.d.ts.map +1 -0
  48. package/dist/{text-DSN3anEh.js → text-CyetbF6p.js} +2039 -1417
  49. package/dist/text-CyetbF6p.js.map +1 -0
  50. package/dist/units-1kRzYShk.js +231 -0
  51. package/dist/units-1kRzYShk.js.map +1 -0
  52. package/dist/units-internal-Dg95w_YD.js +753 -0
  53. package/dist/units-internal-Dg95w_YD.js.map +1 -0
  54. package/dist/zip.d.ts +1 -1
  55. package/package.json +2 -1
  56. package/dist/diagnostics-B0OmTOoz.d.ts.map +0 -1
  57. package/dist/diagnostics-CPVADSL1.js +0 -90
  58. package/dist/diagnostics-CPVADSL1.js.map +0 -1
  59. package/dist/namespaces-Sy_m7Nhd.js.map +0 -1
  60. package/dist/presentation-3rAKKRfm.js.map +0 -1
  61. package/dist/presentation-types-D7cgT-eG.d.ts.map +0 -1
  62. package/dist/shapes-Dyw5bxwO.js.map +0 -1
  63. package/dist/slide-B_aInw3N.d.ts.map +0 -1
  64. package/dist/text-DSN3anEh.js.map +0 -1
  65. package/dist/units-internal-DNvK2I6o.js +0 -896
  66. package/dist/units-internal-DNvK2I6o.js.map +0 -1
@@ -1,9 +1,9 @@
1
+ import { d as emuToInches } from "./units-1kRzYShk.js";
1
2
  import { i as PackageReadError, n as InvalidOptionError, o as UnsupportedFeatureError, t as InternalError } from "./errors-Bvln0H-7.js";
2
- import { mt as emuToInches } from "./units-internal-DNvK2I6o.js";
3
- import { D as voidEl, E as raw, T as el, g as FONT_REL_TYPE, h as FONT_DATA_EXTENSION, m as FONT_DATA_CONTENT_TYPE, n as makeXmlNotesMaster, o as makeXmlNotesSlideSkeleton, p as EMBEDDED_FONT_SLOTS } from "./notes-bWApshah.js";
4
- import { _ as THEME_REL, a as NOTES_MASTER_REL, c as OFFICE_DOCUMENT_REL, d as SLIDE_LAYOUT_CONTENT_TYPE, f as SLIDE_LAYOUT_REL, g as THEME_CONTENT_TYPE, h as SLIDE_REL, i as NOTES_MASTER_CONTENT_TYPE, l as OFFICE_REL, m as SLIDE_MASTER_REL, o as NOTES_SLIDE_CONTENT_TYPE, p as SLIDE_MASTER_CONTENT_TYPE, r as IMAGE_REL, s as NOTES_SLIDE_REL, t as OOXML_NS } from "./namespaces-Sy_m7Nhd.js";
3
+ import { D as voidEl, E as raw, T as el, g as FONT_REL_TYPE, h as FONT_DATA_EXTENSION, m as FONT_DATA_CONTENT_TYPE, n as makeXmlNotesMaster, o as makeXmlNotesSlideSkeleton, p as EMBEDDED_FONT_SLOTS } from "./notes-RLCwdlTE.js";
4
+ import { D as COLOR_MAP_TOKENS, I as THEME_COLOR_SLOTS, _ as THEME_CONTENT_TYPE, a as NOTES_MASTER_REL, c as OFFICE_DOCUMENT_REL, d as SLIDE_CONTENT_TYPE, f as SLIDE_LAYOUT_CONTENT_TYPE, g as SLIDE_REL, h as SLIDE_MASTER_REL, i as NOTES_MASTER_CONTENT_TYPE, l as OFFICE_REL, m as SLIDE_MASTER_CONTENT_TYPE, o as NOTES_SLIDE_CONTENT_TYPE, p as SLIDE_LAYOUT_REL, r as IMAGE_REL, s as NOTES_SLIDE_REL, t as OOXML_NS, v as THEME_REL } from "./namespaces-BVnNu7sg.js";
5
5
  import { ZipWriter, readZip } from "./zip.js";
6
- import { $ as collectElements, A as readPatternFill, At as zipPathToPartName, B as LST_STYLE_LEVELS, Bt as getOrAddChild, C as Picture, Ct as SPPR_SP3D_AFTER, Dt as relsPartNameFor, Et as relativePartName, F as resolveMasterColorContext, Ft as descendantsByTag, G as placeholderInheritedRunProps, Gt as removeAttr, H as placeholderInheritedAnchor, Ht as intValue, I as resolveNotesColorContext, It as escapeXmlAttribute, J as slideDefinesColor, Jt as serializeXml, K as placeholderInheritedXfrm, Kt as removeChildrenByQName, L as resolveSlideColorContext, Lt as firstChild, M as readGradientFill, Mt as boolValue, N as resolveColorElement, Nt as childElements, O as TextFrame, Ot as resolveRelativePartName, P as resolveLayoutColorContext, Pt as createElement, Q as carriedDecorations, R as resolveSlideThemeParts, Rt as firstChildElement, St as SPPR_SCENE3D_AFTER, Tt as partNameToZipPath, U as placeholderInheritedColor, Ut as ownerDocumentOf, V as RUN_PROP_NAMES, Vt as insertInOrder, W as placeholderInheritedListStyles, Wt as parseXml, X as cSldName, Y as slideDefinesProp, Yt as setAttr, Z as cSldOf, _t as SHAPE_AFTER_SPPR, at as fmtEntry, bt as SPPR_FILL_AFTER, ct as parseClrMap, dt as resolveSchemeToken, et as firstShapeChild, ft as styleRefFill, gt as RPR_FILL_AFTER, ht as FILL_CHOICES, it as SCHEME_SLOTS, j as readPictureFill, jt as attr, k as spPrXfrmEmu, kt as resolveSingleRel, l as wrapShapeElement, lt as parseClrScheme, mt as substitutePhClr, n as buildShapes, nt as nvPrOf, ot as intAttr, pt as styleRefLine, q as placeholderOf, qt as replaceInParent, r as findShapeByIdDeep, rt as spTreeOf, st as isA, t as GroupShape, tt as nthShapeChild, u as GraphicFrame, ut as resolveColor, vt as SPPR_AFTER_XFRM, w as AutoShape, wt as partNameExtension, xt as SPPR_LN_AFTER, yt as SPPR_EFFECT_AFTER, zt as getElements } from "./shapes-Dyw5bxwO.js";
6
+ import { $ as collectElements, A as spPrXfrmEmu, At as resolveRelativePartName, B as LST_STYLE_LEVELS, Bt as firstChildElement, Ct as SPPR_LN_AFTER, Dt as partNameToZipPath, Et as partNameExtension, F as resolveLayoutColorContext, Ft as childElements, G as placeholderInheritedRunProps, Gt as ownerDocumentOf, H as placeholderInheritedAnchor, Ht as getOrAddChild, I as resolveMasterColorContext, It as createElement, J as slideDefinesColor, Jt as removeChildrenByQName, K as placeholderInheritedXfrm, Kt as parseXml, L as resolveNotesColorContext, Lt as descendantsByTag, M as readPictureFill, Mt as zipPathToPartName, N as readGradientFill, Nt as attr, Ot as relativePartName, P as resolveColorElement, Pt as boolValue, Q as carriedDecorations, R as resolveSlideColorContext, Rt as escapeXmlAttribute, St as SPPR_FILL_AFTER, T as AutoShape, Tt as SPPR_SP3D_AFTER, U as placeholderInheritedColor, Ut as insertInOrder, V as RUN_PROP_NAMES, Vt as getElements, W as placeholderInheritedListStyles, Wt as intValue, X as cSldName, Xt as serializeXml, Y as slideDefinesProp, Yt as replaceInParent, Z as cSldOf, Zt as setAttr, _t as FILL_CHOICES, at as fmtEntry, bt as SPPR_AFTER_XFRM, ct as parseClrMap, dt as resolveSchemeToken, et as firstShapeChild, ft as styleRefFill, it as SCHEME_SLOTS, j as readPatternFill, jt as resolveSingleRel, k as TextFrame, kt as relsPartNameFor, l as wrapShapeElement, lt as parseClrScheme, mt as substitutePhClr, n as buildShapes, nt as nvPrOf, ot as intAttr, pt as styleRefLine, q as placeholderOf, qt as removeAttr, r as findShapeByIdDeep, rt as spTreeOf, st as isA, t as GroupShape, tt as nthShapeChild, u as GraphicFrame, ut as resolveColor, vt as RPR_FILL_AFTER, w as Picture, wt as SPPR_SCENE3D_AFTER, xt as SPPR_EFFECT_AFTER, yt as SHAPE_AFTER_SPPR, z as resolveSlideThemeParts, zt as firstChild } from "./shapes-BemroU-z.js";
7
7
  //#region src/read/opc/content-types.ts
8
8
  /**
9
9
  * Overlay over `[Content_Types].xml`, used to resolve part content types at
@@ -987,15 +987,108 @@ function readSlideBackground(bg, source, ctx, rels, themeRels) {
987
987
  };
988
988
  }
989
989
  //#endregion
990
- //#region src/read/api/notes.ts
990
+ //#region src/read/api/chrome.ts
991
+ /** Read `<a:latin>`/`<a:ea>`/`<a:cs>` typefaces of a theme font family; empty string ⇒ `null`. */
992
+ function readThemeFontFace(font) {
993
+ const face = (child) => {
994
+ const el = font && firstChild(font, child);
995
+ return el && attr(el, "typeface") || null;
996
+ };
997
+ return {
998
+ latin: face("a:latin"),
999
+ ea: face("a:ea"),
1000
+ cs: face("a:cs")
1001
+ };
1002
+ }
991
1003
  /**
992
- * One placeholder shape (`p:sp`) of a notes slide. A notes slide holds a fixed set:
993
- * the slide thumbnail (`type="sldImg"`), the notes body (`type="body"`), and the
994
- * slide number (`type="sldNum"`). Geometry getters read the placeholder's *own*
995
- * `a:xfrm` and are `null` when it inherits geometry from the notesMaster (the case
996
- * on an authored deck, whose `sldImg`/`sldNum` carry an empty `p:spPr`).
1004
+ * A deck's theme part (`theme1.xml`) as a modeled object. Exposes the two property
1005
+ * tiers a slide resolves against: the {@link colorScheme} (the 12 `a:clrScheme`
1006
+ * slots as literal hex, `a:sysClr` resolved via its `lastClr`) and the
1007
+ * {@link fontScheme} (major/minor Latin/EA/CS faces). Reachable from
1008
+ * {@link SlideMaster.theme} and `Slide.theme`.
1009
+ */
1010
+ var Theme = class {
1011
+ part;
1012
+ constructor(part) {
1013
+ this.part = part;
1014
+ }
1015
+ /** Partname of the theme part. */
1016
+ get partName() {
1017
+ return this.part.partName;
1018
+ }
1019
+ /** Theme name (`a:theme/@name`, e.g. `Office Theme`), or `null` when unnamed. */
1020
+ get name() {
1021
+ const root = this.part.dom.documentElement;
1022
+ return root ? attr(root, "name") : null;
1023
+ }
1024
+ /** Colour-scheme name (`a:clrScheme/@name`, e.g. `Office`), or `null`. */
1025
+ get colorSchemeName() {
1026
+ const clrScheme = this.#clrScheme();
1027
+ return clrScheme ? attr(clrScheme, "name") : null;
1028
+ }
1029
+ /**
1030
+ * The theme colour scheme: the 12 {@link ThemeColorSlot}s resolved to a 6-hex RGB
1031
+ * (an `a:sysClr` slot resolves through its `lastClr`). A slot the theme does not
1032
+ * define reads `null`. Use {@link color} to look one slot up.
1033
+ */
1034
+ get colorScheme() {
1035
+ const parsed = parseClrScheme(this.#clrScheme());
1036
+ const out = {};
1037
+ for (const slot of THEME_COLOR_SLOTS) out[slot] = parsed.get(slot) ?? null;
1038
+ return out;
1039
+ }
1040
+ /** The 6-hex RGB of one colour-scheme slot, or `null` when the theme does not define it. */
1041
+ color(slot) {
1042
+ return parseClrScheme(this.#clrScheme()).get(slot) ?? null;
1043
+ }
1044
+ /** The theme's `a:fontScheme` (major/minor font families), or `null` when the theme declares none. */
1045
+ get fontScheme() {
1046
+ const themeElements = this.#themeElements();
1047
+ const fontScheme = themeElements && firstChild(themeElements, "a:fontScheme");
1048
+ if (!fontScheme) return null;
1049
+ return {
1050
+ name: attr(fontScheme, "name"),
1051
+ major: readThemeFontFace(firstChild(fontScheme, "a:majorFont")),
1052
+ minor: readThemeFontFace(firstChild(fontScheme, "a:minorFont"))
1053
+ };
1054
+ }
1055
+ /** Escape hatch: the underlying `a:theme` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
1056
+ get element_() {
1057
+ return this.part.dom.documentElement;
1058
+ }
1059
+ /** Mark the theme part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
1060
+ markDirty() {
1061
+ this.part.markDirty();
1062
+ }
1063
+ #themeElements() {
1064
+ const root = this.part.dom.documentElement;
1065
+ return root ? firstChild(root, "a:themeElements") : null;
1066
+ }
1067
+ #clrScheme() {
1068
+ const themeElements = this.#themeElements();
1069
+ return themeElements ? firstChild(themeElements, "a:clrScheme") : null;
1070
+ }
1071
+ };
1072
+ /** The placeholder shapes (`p:sp` carrying a `p:ph`) of a master/layout `p:spTree`, in document order. */
1073
+ function placeholderShapes(spTree) {
1074
+ if (!spTree) return [];
1075
+ return getElements(spTree, "p:sp").filter((sp) => placeholderOf(sp) !== null);
1076
+ }
1077
+ /**
1078
+ * One placeholder shape (`p:sp` with a `p:ph`) of a slide master or slide layout —
1079
+ * the definition a slide's placeholder of the same `type`/`idx` inherits geometry
1080
+ * and text style from. Geometry getters read the placeholder's *own* `a:xfrm`,
1081
+ * which a master/layout placeholder carries explicitly (so it round-trips, unlike a
1082
+ * notes placeholder's inherited geometry).
1083
+ *
1084
+ * This is the identity-and-geometry view. The same `p:sp` also appears in
1085
+ * {@link SlideMaster.shapes} / {@link SlideLayout.shapes} as an `AutoShape`, which
1086
+ * additionally carries the paint surface (`resolvedFill`, `resolvedLine`,
1087
+ * `presetGeometry`, `rotation`, `absoluteFrame`) and reports its `p:ph` through
1088
+ * `AutoShape.placeholder`. Read a placeholder here to *place* it; read it there to
1089
+ * *draw* it.
997
1090
  */
998
- var NotesPlaceholder = class {
1091
+ var Placeholder = class {
999
1092
  sp;
1000
1093
  part;
1001
1094
  themeContext;
@@ -1006,22 +1099,18 @@ var NotesPlaceholder = class {
1006
1099
  this.themeContext = themeContext;
1007
1100
  this.relationships = relationships;
1008
1101
  }
1009
- #ph() {
1010
- const nvPr = nvPrOf(this.sp);
1011
- return nvPr ? firstChild(nvPr, "p:ph") : null;
1012
- }
1013
1102
  #cNvPr() {
1014
1103
  const nvSpPr = firstChild(this.sp, "p:nvSpPr");
1015
1104
  return nvSpPr ? firstChild(nvSpPr, "p:cNvPr") : null;
1016
1105
  }
1017
- /** Placeholder type (`p:ph/@type`: `sldImg` | `body` | `sldNum`), or `null` when absent. */
1106
+ /** Placeholder type (`p:ph/@type`: `title` | `body` | `sldNum` | …), or `null` when absent (a body placeholder). */
1018
1107
  get type() {
1019
- const ph = this.#ph();
1108
+ const ph = placeholderOf(this.sp);
1020
1109
  return ph ? attr(ph, "type") : null;
1021
1110
  }
1022
1111
  /** Placeholder index (`p:ph/@idx`), or `null` when unset. */
1023
1112
  get idx() {
1024
- const ph = this.#ph();
1113
+ const ph = placeholderOf(this.sp);
1025
1114
  return ph ? attr(ph, "idx") : null;
1026
1115
  }
1027
1116
  /** Shape name (`p:cNvPr/@name`), or `''` when unnamed. */
@@ -1034,63 +1123,63 @@ var NotesPlaceholder = class {
1034
1123
  const cNvPr = this.#cNvPr();
1035
1124
  return cNvPr ? intValue(attr(cNvPr, "id")) : null;
1036
1125
  }
1037
- /** Left edge in EMU (`a:off/@x`), or `null` when geometry is inherited (no own `a:xfrm`). */
1126
+ /** Left edge in EMU (`a:off/@x`), or `null` when the placeholder carries no own `a:xfrm`. */
1038
1127
  get left() {
1039
1128
  return spPrXfrmEmu(this.sp, "a:off", "x");
1040
1129
  }
1041
- /** Top edge in EMU (`a:off/@y`), or `null` when geometry is inherited. */
1130
+ /** Top edge in EMU (`a:off/@y`), or `null` when the placeholder carries no own `a:xfrm`. */
1042
1131
  get top() {
1043
1132
  return spPrXfrmEmu(this.sp, "a:off", "y");
1044
1133
  }
1045
- /** Width in EMU (`a:ext/@cx`), or `null` when geometry is inherited. */
1134
+ /** Width in EMU (`a:ext/@cx`), or `null` when the placeholder carries no own `a:xfrm`. */
1046
1135
  get width() {
1047
1136
  return spPrXfrmEmu(this.sp, "a:ext", "cx");
1048
1137
  }
1049
- /** Height in EMU (`a:ext/@cy`), or `null` when geometry is inherited. */
1138
+ /** Height in EMU (`a:ext/@cy`), or `null` when the placeholder carries no own `a:xfrm`. */
1050
1139
  get height() {
1051
1140
  return spPrXfrmEmu(this.sp, "a:ext", "cy");
1052
1141
  }
1053
1142
  /**
1054
1143
  * The placeholder's text as a navigable {@link TextFrame} (`p:txBody`), or `null`
1055
- * when it carries no text body (the `sldImg` thumbnail). The body placeholder's
1056
- * runs round-trip a slide's authored notes; the `sldNum` placeholder's frame
1057
- * holds the slide-number `a:fld`, whose value surfaces through `TextFrame.text`.
1144
+ * when it carries no text body. Threaded with the owning master/layout theme
1145
+ * context so a run's own `schemeClr` resolves through `Run.resolvedColor`.
1058
1146
  */
1059
1147
  get textFrame() {
1060
1148
  const txBody = firstChild(this.sp, "p:txBody");
1061
- if (!txBody) return null;
1062
- const placeholder = this.type === "body" ? {
1063
- ph: {
1064
- type: this.type,
1065
- idx: this.idx ?? "0"
1066
- },
1067
- flatten: this.themeContext
1068
- } : void 0;
1069
- return new TextFrame(txBody, this.part, this.themeContext, placeholder, this.relationships);
1070
- }
1071
- /** The placeholder's flattened text (paragraphs joined by `\n`), or `''` when it has no text body. */
1072
- get text() {
1073
- return this.textFrame?.text ?? "";
1149
+ return txBody ? new TextFrame(txBody, this.part, this.themeContext, void 0, this.relationships) : null;
1074
1150
  }
1075
1151
  /** Escape hatch: the underlying `p:sp` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
1076
1152
  get element_() {
1077
1153
  return this.sp;
1078
1154
  }
1079
- /** Mark the owning notes-slide part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
1155
+ /** Mark the owning part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
1080
1156
  markDirty() {
1081
1157
  this.part.markDirty();
1082
1158
  }
1083
1159
  };
1084
1160
  /**
1085
- * A slide's speaker-notes slide (`notesSlideN.xml`) as a modeled object — the deep
1086
- * companion to {@link import('./slide.js').Slide.notesText}. Exposes the notes
1087
- * shape tree as {@link NotesPlaceholder}s ({@link body}/{@link slideImage}/
1088
- * {@link slideNumber}) plus the body {@link textFrame} the older getters flatten.
1161
+ * The shared body of a slide master and a slide layout — the two template tiers a slide
1162
+ * inherits from.
1089
1163
  *
1090
- * The theme context (notesMaster → `theme2.xml`) is resolved once and shared across
1091
- * every placeholder's text frame.
1164
+ * Both are an OPC part holding a `p:cSld` with a shape tree and a resolvable theme context, and
1165
+ * both were written out in full, member for member: `partName`, `relationships`, `name`,
1166
+ * `shapes`, `shapeByIdDeep`, `placeholders`, `themeContext` and the two DOM lookups behind them
1167
+ * were character-for-character identical in the two classes. Nothing about that was broken, but
1168
+ * it meant every accessor added to a template tier had to be added twice, and the ones meant to
1169
+ * agree could quietly stop agreeing.
1170
+ *
1171
+ * What genuinely differs stays on the subclasses. `theme` differs because a layout has to walk
1172
+ * to its master first. `background` differs in the tier label it decodes with and in where it
1173
+ * finds the theme part. And the context itself differs, which is why {@link resolveThemeContext}
1174
+ * is abstract: a master resolves its own `p:clrMap` + theme, a layout walks layout → master →
1175
+ * theme. The caching around it is shared, so a subclass supplies the walk and nothing else.
1176
+ *
1177
+ * `Slide` deliberately does not extend this. It is a shape host too, but it is constructed from
1178
+ * a `Presentation` rather than an `OpcPackage`, reaches its relationships through that, and
1179
+ * reports an unnamed slide as `null` where a template tier reports `''` — three differences in
1180
+ * the same handful of members, which is more contortion than the sharing is worth.
1092
1181
  */
1093
- var NotesSlide = class {
1182
+ var TemplatePart = class {
1094
1183
  opc;
1095
1184
  part;
1096
1185
  #themeContext;
@@ -1098,72 +1187,303 @@ var NotesSlide = class {
1098
1187
  this.opc = opc;
1099
1188
  this.part = part;
1100
1189
  }
1101
- /** Partname of the notes slide part. */
1190
+ /** Partname of this part. */
1102
1191
  get partName() {
1103
1192
  return this.part.partName;
1104
1193
  }
1194
+ /** This part's relationships (its theme or master, image embeds, hyperlinks, …). */
1195
+ get relationships() {
1196
+ return this.opc.relationshipsFor(this.partName);
1197
+ }
1198
+ /** The authoring name (`p:cSld/@name`), or `''` when unnamed. */
1199
+ get name() {
1200
+ const cSld = this.cSld();
1201
+ return (cSld && attr(cSld, "name")) ?? "";
1202
+ }
1105
1203
  /**
1106
- * The notes theme context, resolved through the notesMaster → `theme2.xml` chain
1107
- * and cached. Backs {@link NotesPlaceholder.textFrame}'s `Run.resolvedColor`.
1204
+ * Every shape in this tier's `p:cSld/p:spTree`, in document order — the same `AnyShape`
1205
+ * union `Slide.shapes` returns, so shape-walking code applies unchanged.
1206
+ *
1207
+ * {@link placeholders} is the filtered view of the same tree.
1108
1208
  */
1109
- themeContext() {
1110
- return this.#themeContext ??= resolveNotesColorContext(this.opc, this.partName);
1209
+ get shapes() {
1210
+ const spTree = this.spTree();
1211
+ return spTree ? buildShapes(spTree, this) : [];
1111
1212
  }
1112
- /** The placeholders (`p:sp`) in the notes shape tree, in document order. */
1213
+ /** The shape anywhere in this tier's tree with the given drawing id, or `undefined`. */
1214
+ shapeByIdDeep(id) {
1215
+ return findShapeByIdDeep(this.shapes, id);
1216
+ }
1217
+ /** This tier's placeholder shapes (`p:sp` carrying a `p:ph`), in document order. */
1113
1218
  get placeholders() {
1114
- const spTree = this.#spTree();
1115
- if (!spTree) return [];
1116
1219
  const ctx = this.themeContext();
1117
- const rels = this.opc.relationshipsFor(this.partName);
1118
- return getElements(spTree, "p:sp").map((sp) => new NotesPlaceholder(sp, this.part, ctx, rels));
1220
+ const rels = this.relationships;
1221
+ return placeholderShapes(this.spTree()).map((sp) => new Placeholder(sp, this.part, ctx, rels));
1119
1222
  }
1120
- /** The notes body placeholder (`p:ph type="body"`), or `null` when the notes slide has none. */
1121
- get body() {
1122
- return this.#byType("body");
1223
+ /** This tier's colour/font context, resolved once and cached. Backs each {@link Placeholder}'s text frame. */
1224
+ themeContext() {
1225
+ return this.#themeContext ??= this.resolveThemeContext();
1123
1226
  }
1124
- /** The slide-thumbnail placeholder (`p:ph type="sldImg"`), or `null` when absent. */
1125
- get slideImage() {
1126
- return this.#byType("sldImg");
1227
+ /** The `p:cSld` of this part, or `null`. */
1228
+ cSld() {
1229
+ return cSldOf(this.part.dom.documentElement);
1127
1230
  }
1128
- /** The slide-number placeholder (`p:ph type="sldNum"`, carrying the slide-number field), or `null`. */
1129
- get slideNumber() {
1130
- return this.#byType("sldNum");
1231
+ /** The `p:cSld/p:spTree` of this part, or `null`. */
1232
+ spTree() {
1233
+ return spTreeOf(this.part.dom.documentElement);
1131
1234
  }
1132
- /** The notes body as a navigable {@link TextFrame}, or `null` when there is no body text frame. */
1133
- get textFrame() {
1134
- return this.body?.textFrame ?? null;
1235
+ /** The part this one's first relationship of `type` points at, or `null`. */
1236
+ relTarget(type) {
1237
+ const rels = this.relationships;
1238
+ const rel = rels.byType(type)[0];
1239
+ return rel ? this.opc.part(rels.resolveTarget(rel.id)) ?? null : null;
1135
1240
  }
1136
- /** The notes body text flattened (paragraphs joined by `\n`), or `''` when the body is empty/absent. */
1137
- get text() {
1138
- return this.body?.text ?? "";
1241
+ };
1242
+ /**
1243
+ * A deck's slide master (`slideMasterN.xml`) as a modeled object. Exposes the
1244
+ * property tiers a bound slide inherits: the {@link colorMap} (token → theme slot),
1245
+ * its {@link theme}, its `shapes` and the `placeholders` subset of them, its own
1246
+ * {@link background}, and the {@link layouts} that build on it. Reachable from
1247
+ * {@link SlideLayout.master} and `Slide.master`.
1248
+ *
1249
+ * `shapes` is where a template's non-placeholder furniture lives: the header band, the
1250
+ * rule under the title, the logo. Whether a given slide draws them is
1251
+ * `Slide.showMasterSp` (and, for a layout, {@link SlideLayout.showMasterSp}).
1252
+ */
1253
+ var SlideMaster = class extends TemplatePart {
1254
+ /** The master's theme (via its `theme` relationship), or `null` when absent. */
1255
+ get theme() {
1256
+ const themePart = this.relTarget(THEME_REL);
1257
+ return themePart ? new Theme(themePart) : null;
1139
1258
  }
1140
- #byType(type) {
1141
- return this.placeholders.find((ph) => ph.type === type) ?? null;
1259
+ /**
1260
+ * The master's colour map (`p:clrMap`): each of the 12 {@link ColorMapToken}s
1261
+ * mapped to the {@link ThemeColorSlot} it resolves through. This is the indirection
1262
+ * that lets a slide's `schemeClr val="tx1"` reach a theme slot (e.g. `tx1` → `dk1`).
1263
+ * A token the map omits reads `null`.
1264
+ */
1265
+ get colorMap() {
1266
+ const root = this.part.dom.documentElement;
1267
+ const parsed = parseClrMap(root ? firstChild(root, "p:clrMap") : null);
1268
+ const out = {};
1269
+ for (const token of COLOR_MAP_TOKENS) out[token] = parsed.get(token) ?? null;
1270
+ return out;
1142
1271
  }
1143
- #spTree() {
1144
- return spTreeOf(this.part.dom.documentElement);
1272
+ /** The layouts built on this master (via `p:sldLayoutIdLst` → its relationships), in list order. */
1273
+ get layouts() {
1274
+ const root = this.part.dom.documentElement;
1275
+ const lst = root && firstChild(root, "p:sldLayoutIdLst");
1276
+ if (!lst) return [];
1277
+ const rels = this.relationships;
1278
+ const out = [];
1279
+ for (const entry of getElements(lst, "p:sldLayoutId")) {
1280
+ const relId = attr(entry, "r:id");
1281
+ const part = relId ? this.opc.part(rels.resolveTarget(relId)) : null;
1282
+ if (part) out.push(new SlideLayout(this.opc, part));
1283
+ }
1284
+ return out;
1285
+ }
1286
+ /**
1287
+ * The master's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
1288
+ * none. Scoped to the master's own element — a slide's *effective* background,
1289
+ * resolved through the slide → layout → master chain, is `Slide.background`.
1290
+ */
1291
+ get background() {
1292
+ const bg = backgroundElementOf(this.part.dom.documentElement);
1293
+ if (!bg) return null;
1294
+ const themePart = this.relTarget(THEME_REL);
1295
+ return readSlideBackground(bg, "master", this.themeContext(), this.relationships, themePart ? this.opc.relationshipsFor(themePart.partName) : null);
1296
+ }
1297
+ /** A master's context is its own `p:clrMap` plus its theme — no walk beyond this part. */
1298
+ resolveThemeContext() {
1299
+ return resolveMasterColorContext(this.opc, this.partName);
1145
1300
  }
1146
1301
  };
1147
- //#endregion
1148
- //#region src/read/api/ops/deck-target.ts
1149
1302
  /**
1150
- * `ppt/_rels/presentation.xml.rels` -- the deck's own relationship part.
1303
+ * A deck's slide layout (`slideLayoutN.xml`) as a modeled object — the gallery entry
1304
+ * a slide binds to. Exposes its {@link name}, its {@link type} (import-only; the
1305
+ * writer authors none), its {@link master} (and, through it, the {@link theme}), its
1306
+ * `shapes` and the `placeholders` subset of them, and its own {@link background}.
1307
+ * Reachable from `Slide.layout` and {@link SlideMaster.layouts}.
1151
1308
  *
1152
- * Every part that masters, layouts, the notes master and embedded fonts hang off
1153
- * is reached through here, so the two-step `dest.presentationPart` ->
1154
- * `dest.opc.relationshipsFor(part.partName)` was written out at every one of
1155
- * those wiring sites. Naming it once means a call site says which rels it wants
1156
- * rather than how to find them.
1157
- * @param {DeckTarget} dest - the destination deck
1158
- * @returns {Relationships} the presentation part's relationships
1309
+ * The write API authors non-placeholder shapes into a layout: every
1310
+ * `defineSlideMaster({ objects })` member that is not a `placeholder` — a `rect`, a
1311
+ * `line`, an `image`, a `chart`, a `text` box — lands in this tier's tree.
1159
1312
  */
1160
- function presentationRels(dest) {
1161
- return dest.opc.relationshipsFor(dest.presentationPart.partName);
1162
- }
1163
- //#endregion
1164
- //#region src/read/api/ops/master-registry.ts
1165
- /**
1166
- * Slide-master bookkeeping in `presentation.xml` and in a master's own
1313
+ var SlideLayout = class extends TemplatePart {
1314
+ /**
1315
+ * The layout type (`p:sldLayout/@type`, e.g. `title` | `obj` | `blank`), or `null`
1316
+ * when the layout declares none. Import-only: the writer authors no `@type`, so an
1317
+ * authored deck reads `null`; an imported deck carries PowerPoint's value.
1318
+ */
1319
+ get type() {
1320
+ const root = this.part.dom.documentElement;
1321
+ return root ? attr(root, "type") : null;
1322
+ }
1323
+ /**
1324
+ * Whether this layout draws the master's non-placeholder shapes
1325
+ * (`p:sldLayout/@showMasterSp`). `xsd:boolean` defaulting to `true`, so an absent
1326
+ * attribute means shown — the layout-tier counterpart of `Slide.showMasterSp`,
1327
+ * and the one PowerPoint actually writes on a section-divider or full-bleed
1328
+ * layout. A renderer resolving what to paint under a slide's own shapes has to
1329
+ * consult both: the slide's flag, then its layout's.
1330
+ */
1331
+ get showMasterSp() {
1332
+ const root = this.part.dom.documentElement;
1333
+ return boolValue(root && attr(root, "showMasterSp")) !== false;
1334
+ }
1335
+ /** The master this layout is built on (via its `slideMaster` relationship), or `null` when absent. */
1336
+ get master() {
1337
+ const rels = this.relationships;
1338
+ const rel = rels.byType(SLIDE_MASTER_REL)[0];
1339
+ const part = rel ? this.opc.part(rels.resolveTarget(rel.id)) : null;
1340
+ return part ? new SlideMaster(this.opc, part) : null;
1341
+ }
1342
+ /** The theme this layout resolves against, via its {@link master}, or `null`. */
1343
+ get theme() {
1344
+ return this.master?.theme ?? null;
1345
+ }
1346
+ /**
1347
+ * The layout's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
1348
+ * none. A layout usually authors one — an explicit fill (`p:bgPr`) or a
1349
+ * theme-indexed reference (`p:bgRef`). The slide's *effective* background is
1350
+ * `Slide.background`.
1351
+ */
1352
+ get background() {
1353
+ const bg = backgroundElementOf(this.part.dom.documentElement);
1354
+ if (!bg) return null;
1355
+ const themePart = this.master?.theme?.part ?? null;
1356
+ return readSlideBackground(bg, "layout", this.themeContext(), this.relationships, themePart ? this.opc.relationshipsFor(themePart.partName) : null);
1357
+ }
1358
+ /** A layout's context is walked layout → master → theme, which is why it is not the master's. */
1359
+ resolveThemeContext() {
1360
+ return resolveLayoutColorContext(this.opc, this.partName);
1361
+ }
1362
+ };
1363
+ //#endregion
1364
+ //#region src/read/api/notes.ts
1365
+ /**
1366
+ * One placeholder shape (`p:sp`) of a notes slide. A notes slide holds a fixed set:
1367
+ * the slide thumbnail (`type="sldImg"`), the notes body (`type="body"`), and the
1368
+ * slide number (`type="sldNum"`). Geometry getters read the placeholder's *own*
1369
+ * `a:xfrm` and are `null` when it inherits geometry from the notesMaster (the case
1370
+ * on an authored deck, whose `sldImg`/`sldNum` carry an empty `p:spPr`).
1371
+ *
1372
+ * A notes placeholder IS a {@link Placeholder} — identity, geometry and the escape hatch are
1373
+ * the same twelve members read off the same `p:sp`, down to `p:ph` being found the same way —
1374
+ * so it extends one rather than repeating it. Two things are its own: the flattened
1375
+ * {@link text} convenience, and a {@link textFrame} that threads the notesMaster inheritance
1376
+ * context onto the body frame.
1377
+ */
1378
+ var NotesPlaceholder = class extends Placeholder {
1379
+ /**
1380
+ * The placeholder's text as a navigable {@link TextFrame} (`p:txBody`), or `null`
1381
+ * when it carries no text body (the `sldImg` thumbnail). The body placeholder's
1382
+ * runs round-trip a slide's authored notes; the `sldNum` placeholder's frame
1383
+ * holds the slide-number `a:fld`, whose value surfaces through `TextFrame.text`.
1384
+ *
1385
+ * Overrides {@link Placeholder.textFrame} for the inheritance context alone — see below.
1386
+ */
1387
+ get textFrame() {
1388
+ const txBody = firstChild(this.sp, "p:txBody");
1389
+ if (!txBody) return null;
1390
+ const placeholder = this.type === "body" ? {
1391
+ ph: {
1392
+ type: this.type,
1393
+ idx: this.idx ?? "0"
1394
+ },
1395
+ flatten: this.themeContext
1396
+ } : void 0;
1397
+ return new TextFrame(txBody, this.part, this.themeContext, placeholder, this.relationships);
1398
+ }
1399
+ /** The placeholder's flattened text (paragraphs joined by `\n`), or `''` when it has no text body. */
1400
+ get text() {
1401
+ return this.textFrame?.text ?? "";
1402
+ }
1403
+ };
1404
+ /**
1405
+ * A slide's speaker-notes slide (`notesSlideN.xml`) as a modeled object — the deep
1406
+ * companion to {@link import('./slide.js').Slide.notesText}. Exposes the notes
1407
+ * shape tree as {@link NotesPlaceholder}s ({@link body}/{@link slideImage}/
1408
+ * {@link slideNumber}) plus the body {@link textFrame} the older getters flatten.
1409
+ *
1410
+ * The theme context (notesMaster → `theme2.xml`) is resolved once and shared across
1411
+ * every placeholder's text frame.
1412
+ */
1413
+ var NotesSlide = class {
1414
+ opc;
1415
+ part;
1416
+ #themeContext;
1417
+ constructor(opc, part) {
1418
+ this.opc = opc;
1419
+ this.part = part;
1420
+ }
1421
+ /** Partname of the notes slide part. */
1422
+ get partName() {
1423
+ return this.part.partName;
1424
+ }
1425
+ /**
1426
+ * The notes theme context, resolved through the notesMaster → `theme2.xml` chain
1427
+ * and cached. Backs {@link NotesPlaceholder.textFrame}'s `Run.resolvedColor`.
1428
+ */
1429
+ themeContext() {
1430
+ return this.#themeContext ??= resolveNotesColorContext(this.opc, this.partName);
1431
+ }
1432
+ /** The placeholders (`p:sp`) in the notes shape tree, in document order. */
1433
+ get placeholders() {
1434
+ const spTree = this.#spTree();
1435
+ if (!spTree) return [];
1436
+ const ctx = this.themeContext();
1437
+ const rels = this.opc.relationshipsFor(this.partName);
1438
+ return getElements(spTree, "p:sp").map((sp) => new NotesPlaceholder(sp, this.part, ctx, rels));
1439
+ }
1440
+ /** The notes body placeholder (`p:ph type="body"`), or `null` when the notes slide has none. */
1441
+ get body() {
1442
+ return this.#byType("body");
1443
+ }
1444
+ /** The slide-thumbnail placeholder (`p:ph type="sldImg"`), or `null` when absent. */
1445
+ get slideImage() {
1446
+ return this.#byType("sldImg");
1447
+ }
1448
+ /** The slide-number placeholder (`p:ph type="sldNum"`, carrying the slide-number field), or `null`. */
1449
+ get slideNumber() {
1450
+ return this.#byType("sldNum");
1451
+ }
1452
+ /** The notes body as a navigable {@link TextFrame}, or `null` when there is no body text frame. */
1453
+ get textFrame() {
1454
+ return this.body?.textFrame ?? null;
1455
+ }
1456
+ /** The notes body text flattened (paragraphs joined by `\n`), or `''` when the body is empty/absent. */
1457
+ get text() {
1458
+ return this.body?.text ?? "";
1459
+ }
1460
+ #byType(type) {
1461
+ return this.placeholders.find((ph) => ph.type === type) ?? null;
1462
+ }
1463
+ #spTree() {
1464
+ return spTreeOf(this.part.dom.documentElement);
1465
+ }
1466
+ };
1467
+ //#endregion
1468
+ //#region src/read/api/ops/deck-target.ts
1469
+ /**
1470
+ * `ppt/_rels/presentation.xml.rels` -- the deck's own relationship part.
1471
+ *
1472
+ * Every part that masters, layouts, the notes master and embedded fonts hang off
1473
+ * is reached through here, so the two-step `dest.presentationPart` ->
1474
+ * `dest.opc.relationshipsFor(part.partName)` was written out at every one of
1475
+ * those wiring sites. Naming it once means a call site says which rels it wants
1476
+ * rather than how to find them.
1477
+ * @param {DeckTarget} dest - the destination deck
1478
+ * @returns {Relationships} the presentation part's relationships
1479
+ */
1480
+ function presentationRels(dest) {
1481
+ return dest.opc.relationshipsFor(dest.presentationPart.partName);
1482
+ }
1483
+ //#endregion
1484
+ //#region src/read/api/ops/master-registry.ts
1485
+ /**
1486
+ * Slide-master bookkeeping in `presentation.xml` and in a master's own
1167
1487
  * `p:sldLayoutIdLst`: registering a master so renderers treat it as active,
1168
1488
  * promoting masters to the front of the id list, allocating ids out of the shared
1169
1489
  * master/layout id space, and emptying a freshly-copied master's layout list.
@@ -1391,6 +1711,148 @@ function duplicateSubtree(opc, partName, copies) {
1391
1711
  return fresh;
1392
1712
  }
1393
1713
  //#endregion
1714
+ //#region src/read/api/ops/part-reuse.ts
1715
+ /**
1716
+ * Recognising a part the destination deck **already holds**, so an import binds to it
1717
+ * instead of copying it in again.
1718
+ *
1719
+ * The case this exists for is the deck templated from its own source. `fromTemplate`
1720
+ * keeps a package's masters, layouts and theme byte-identical and strips only its
1721
+ * slides, so a script that then imports a slide back out of that same file hands
1722
+ * `copyPart` a `slideLayout -> slideMaster -> theme` subgraph whose every part is
1723
+ * already in the destination under its own partname. The copy registry maps *source*
1724
+ * partnames to copies this import made and knows nothing about what the destination
1725
+ * arrived with, so each of those parts was copied again: the output grew a duplicate
1726
+ * layout and master per imported slide, visible in PowerPoint's layout picker and
1727
+ * enough to make a later `appendSlides({ layout: <name> })` ambiguous.
1728
+ *
1729
+ * The test is deliberately strict, because the cost of getting it wrong is a slide
1730
+ * bound to chrome that only resembles what it asked for. A part is reusable only when
1731
+ * the destination holds it at the *same partname*, with the same content type, the same
1732
+ * bytes (as they would be written, so an edited destination part disqualifies itself),
1733
+ * the same relationships, and the same rule applied recursively to everything those
1734
+ * relationships reach. Byte-equality all the way down means "reuse" and "copy" produce
1735
+ * the same rendering; anything less falls back to the copy, which is always correct and
1736
+ * merely wasteful.
1737
+ *
1738
+ * Two further conditions have nothing to do with identity and everything to do with the
1739
+ * destination being a real deck: a reused master must already be registered in
1740
+ * `presentation.xml`, and a reused layout must already sit in a registered master's
1741
+ * `p:sldLayoutIdLst`. An identical part that is an orphan in the destination is not
1742
+ * something to bind a slide to, and the copy path (which registers and links what it
1743
+ * copies) is the right answer there.
1744
+ *
1745
+ * Reuse mutates nothing, which is what lets it hold across repeated imports: the
1746
+ * destination's chrome stays byte-identical, so the second import recognises it exactly
1747
+ * as the first one did.
1748
+ */
1749
+ /**
1750
+ * Whether the destination already holds `sourcePartName`'s subgraph, part for part and
1751
+ * byte for byte, in a state an imported slide can bind to.
1752
+ *
1753
+ * @param dest the deck being imported into
1754
+ * @param source the package being imported out of
1755
+ * @param sourcePartName the part the copy is about to make
1756
+ * @return `true` when the copy can be skipped and the existing part used
1757
+ */
1758
+ function destinationAlreadyHolds(dest, source, sourcePartName) {
1759
+ const part = dest.opc.part(sourcePartName);
1760
+ if (!part || part.contentType === "application/vnd.openxmlformats-officedocument.presentationml.slide+xml") return false;
1761
+ if (!identicalSubgraph(dest.opc, source, sourcePartName, /* @__PURE__ */ new Set())) return false;
1762
+ return wiredIntoDeck(dest, sourcePartName);
1763
+ }
1764
+ /**
1765
+ * `partName` and everything it reaches are the same in both packages: same content type,
1766
+ * same body bytes, same relationships, recursively.
1767
+ *
1768
+ * The destination side is compared as it would be **written** (`serialize()`), not as it
1769
+ * was read, so a part some earlier edit marked dirty is treated as different even when
1770
+ * the edit was a no-op. Under-reusing costs a duplicated part; over-reusing binds a slide
1771
+ * to something else's chrome.
1772
+ *
1773
+ * The traversal skips exactly what `copyPart` skips — the notes relationship, and a
1774
+ * master's relationships to its layouts — so it asks about the subgraph the copy would
1775
+ * actually have made. Skipping the master's layout list is what keeps one edited layout
1776
+ * from disqualifying every *other* layout that shares its master.
1777
+ */
1778
+ function identicalSubgraph(dest, source, partName, visited) {
1779
+ if (visited.has(partName)) return true;
1780
+ visited.add(partName);
1781
+ const sourcePart = source.part(partName);
1782
+ const destPart = dest.part(partName);
1783
+ if (!sourcePart || !destPart) return false;
1784
+ if (sourcePart.contentType !== destPart.contentType) return false;
1785
+ if (!bytesEqual(sourcePart.bytes, destPart.serialize())) return false;
1786
+ const sourceRels = [...source.relationshipsFor(partName)];
1787
+ const destRels = [...dest.relationshipsFor(partName)];
1788
+ if (sourceRels.length !== destRels.length) return false;
1789
+ const isMaster = sourcePart.contentType === SLIDE_MASTER_CONTENT_TYPE;
1790
+ const destById = new Map(destRels.map((rel) => [rel.id, rel]));
1791
+ for (const rel of sourceRels) {
1792
+ const counterpart = destById.get(rel.id);
1793
+ if (!counterpart) return false;
1794
+ if (counterpart.type !== rel.type || counterpart.targetMode !== rel.targetMode) return false;
1795
+ if (rel.targetMode === "External") {
1796
+ if (counterpart.target !== rel.target) return false;
1797
+ continue;
1798
+ }
1799
+ const target = source.relationshipsFor(partName).resolveTarget(rel.id);
1800
+ if (dest.relationshipsFor(partName).resolveTarget(rel.id) !== target) return false;
1801
+ if (rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide") continue;
1802
+ if (isMaster && rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") continue;
1803
+ if (!identicalSubgraph(dest, source, target, visited)) return false;
1804
+ }
1805
+ return true;
1806
+ }
1807
+ /**
1808
+ * Whether the destination's copy of this part is wired into the deck, for the two kinds
1809
+ * where "present in the package" is not the same as "usable".
1810
+ *
1811
+ * A master absent from `p:sldMasterIdLst` is inert: renderers ignore its background and
1812
+ * shape tree, so a slide bound through it renders blank. A layout absent from a
1813
+ * registered master's `p:sldLayoutIdLst` is not in the deck's gallery, so `layouts()`
1814
+ * cannot name it and `appendSlides` cannot bind to it. Every other kind (theme, media,
1815
+ * table styles) is reached by relationship alone and needs no registration.
1816
+ */
1817
+ function wiredIntoDeck(dest, partName) {
1818
+ const contentType = dest.opc.part(partName)?.contentType;
1819
+ if (contentType === "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml") return registeredMasters(dest).includes(partName);
1820
+ if (contentType === "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml") return registeredMasters(dest).some((master) => galleryOf(dest, master).includes(partName));
1821
+ return true;
1822
+ }
1823
+ /** The destination's masters in `p:sldMasterIdLst` order — the ones renderers treat as active. */
1824
+ function registeredMasters(dest) {
1825
+ const root = dest.presentationPart.dom.documentElement;
1826
+ const lst = root && firstChild(root, "p:sldMasterIdLst");
1827
+ if (!lst) return [];
1828
+ const rels = presentationRels(dest);
1829
+ return idListTargets(lst, "p:sldMasterId", (relId) => rels.resolveTarget(relId));
1830
+ }
1831
+ /** One master's layouts in `p:sldLayoutIdLst` order — its slice of the deck's gallery. */
1832
+ function galleryOf(dest, masterPartName) {
1833
+ const root = dest.opc.part(masterPartName)?.dom.documentElement;
1834
+ const lst = root && firstChild(root, "p:sldLayoutIdLst");
1835
+ if (!lst) return [];
1836
+ const rels = dest.opc.relationshipsFor(masterPartName);
1837
+ return idListTargets(lst, "p:sldLayoutId", (relId) => rels.resolveTarget(relId));
1838
+ }
1839
+ /** Resolve every `@r:id` in an id list to its partname, skipping entries that carry none. */
1840
+ function idListTargets(lst, qname, resolve) {
1841
+ const out = [];
1842
+ for (const entry of getElements(lst, qname)) {
1843
+ const relId = attr(entry, "r:id");
1844
+ if (relId) out.push(resolve(relId));
1845
+ }
1846
+ return out;
1847
+ }
1848
+ /** Byte-for-byte equality of two part bodies. */
1849
+ function bytesEqual(a, b) {
1850
+ if (a === b) return true;
1851
+ if (a.length !== b.length) return false;
1852
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
1853
+ return true;
1854
+ }
1855
+ //#endregion
1394
1856
  //#region src/read/api/ops/part-copy.ts
1395
1857
  /** Open an ownership scope for one page copy. */
1396
1858
  function newOwnedScope() {
@@ -1448,7 +1910,7 @@ function copySlidePart(ctx, sourcePartName) {
1448
1910
  * from outside, as the rebinding import paths do for the page they build
1449
1911
  * themselves.
1450
1912
  */
1451
- function copyPart(ctx, sourcePartName, owned) {
1913
+ function copyPart(ctx, sourcePartName, owned, allowReuse = false) {
1452
1914
  if (owned) {
1453
1915
  const alreadyOwned = owned.get(sourcePartName);
1454
1916
  if (alreadyOwned !== void 0) return alreadyOwned;
@@ -1457,6 +1919,12 @@ function copyPart(ctx, sourcePartName, owned) {
1457
1919
  const selectedDest = ctx.selection?.destinations.get(sourcePartName);
1458
1920
  if (existing && (selectedDest === void 0 || existing === selectedDest)) return existing;
1459
1921
  }
1922
+ if (allowReuse && !owned && !ctx.selection?.destinations.has(sourcePartName)) {
1923
+ if (destinationAlreadyHolds(ctx.dest, ctx.source, sourcePartName)) {
1924
+ ctx.registry.set(sourcePartName, sourcePartName);
1925
+ return sourcePartName;
1926
+ }
1927
+ }
1460
1928
  const sourcePart = ctx.source.part(sourcePartName);
1461
1929
  if (!sourcePart) throw new PackageReadError("package/part-missing", `importSlide: source package has no part ${sourcePartName}`);
1462
1930
  const newPartName = owned ? ctx.dest.opc.reservePartNameLike(sourcePartName) : ctx.selection?.destinations.get(sourcePartName) ?? ctx.dest.opc.reservePartNameLike(sourcePartName);
@@ -1465,6 +1933,7 @@ function copyPart(ctx, sourcePartName, owned) {
1465
1933
  else ctx.registry.set(sourcePartName, newPartName);
1466
1934
  const scope = owned ?? (ctx.selection?.destinations.has(sourcePartName) ? newOwnedScope() : void 0);
1467
1935
  const isMaster = sourcePart.contentType === SLIDE_MASTER_CONTENT_TYPE;
1936
+ const isPage = sourcePart.contentType === SLIDE_CONTENT_TYPE;
1468
1937
  const sourceRels = ctx.source.relationshipsFor(sourcePartName);
1469
1938
  const targetRels = ctx.dest.opc.relationshipsFor(newPartName);
1470
1939
  for (const rel of sourceRels) {
@@ -1474,7 +1943,7 @@ function copyPart(ctx, sourcePartName, owned) {
1474
1943
  targetRels.addWithId(rel.id, rel.type, rel.target, "External");
1475
1944
  continue;
1476
1945
  }
1477
- const newTargetPartName = copyPart(ctx, sourceRels.resolveTarget(rel.id), isSharedByPageCopies(rel.type) ? void 0 : scope);
1946
+ const newTargetPartName = copyPart(ctx, sourceRels.resolveTarget(rel.id), isSharedByPageCopies(rel.type) ? void 0 : scope, isPage);
1478
1947
  targetRels.addWithId(rel.id, rel.type, relativePartName(newPartName, newTargetPartName));
1479
1948
  }
1480
1949
  if (isMaster) {
@@ -2018,427 +2487,6 @@ function readTagsForPart(opc, ownerPartName) {
2018
2487
  return out;
2019
2488
  }
2020
2489
  //#endregion
2021
- //#region src/read/api/chrome.ts
2022
- const THEME_COLOR_SLOTS = [
2023
- "dk1",
2024
- "lt1",
2025
- "dk2",
2026
- "lt2",
2027
- "accent1",
2028
- "accent2",
2029
- "accent3",
2030
- "accent4",
2031
- "accent5",
2032
- "accent6",
2033
- "hlink",
2034
- "folHlink"
2035
- ];
2036
- const COLOR_MAP_TOKENS = [
2037
- "bg1",
2038
- "tx1",
2039
- "bg2",
2040
- "tx2",
2041
- "accent1",
2042
- "accent2",
2043
- "accent3",
2044
- "accent4",
2045
- "accent5",
2046
- "accent6",
2047
- "hlink",
2048
- "folHlink"
2049
- ];
2050
- /** Read `<a:latin>`/`<a:ea>`/`<a:cs>` typefaces of a theme font family; empty string ⇒ `null`. */
2051
- function readThemeFontFace(font) {
2052
- const face = (child) => {
2053
- const el = font && firstChild(font, child);
2054
- return el && attr(el, "typeface") || null;
2055
- };
2056
- return {
2057
- latin: face("a:latin"),
2058
- ea: face("a:ea"),
2059
- cs: face("a:cs")
2060
- };
2061
- }
2062
- /**
2063
- * A deck's theme part (`theme1.xml`) as a modeled object. Exposes the two property
2064
- * tiers a slide resolves against: the {@link colorScheme} (the 12 `a:clrScheme`
2065
- * slots as literal hex, `a:sysClr` resolved via its `lastClr`) and the
2066
- * {@link fontScheme} (major/minor Latin/EA/CS faces). Reachable from
2067
- * {@link SlideMaster.theme} and `Slide.theme`.
2068
- */
2069
- var Theme = class {
2070
- part;
2071
- constructor(part) {
2072
- this.part = part;
2073
- }
2074
- /** Partname of the theme part. */
2075
- get partName() {
2076
- return this.part.partName;
2077
- }
2078
- /** Theme name (`a:theme/@name`, e.g. `Office Theme`), or `null` when unnamed. */
2079
- get name() {
2080
- const root = this.part.dom.documentElement;
2081
- return root ? attr(root, "name") : null;
2082
- }
2083
- /** Colour-scheme name (`a:clrScheme/@name`, e.g. `Office`), or `null`. */
2084
- get colorSchemeName() {
2085
- const clrScheme = this.#clrScheme();
2086
- return clrScheme ? attr(clrScheme, "name") : null;
2087
- }
2088
- /**
2089
- * The theme colour scheme: the 12 {@link ThemeColorSlot}s resolved to a 6-hex RGB
2090
- * (an `a:sysClr` slot resolves through its `lastClr`). A slot the theme does not
2091
- * define reads `null`. Use {@link color} to look one slot up.
2092
- */
2093
- get colorScheme() {
2094
- const parsed = parseClrScheme(this.#clrScheme());
2095
- const out = {};
2096
- for (const slot of THEME_COLOR_SLOTS) out[slot] = parsed.get(slot) ?? null;
2097
- return out;
2098
- }
2099
- /** The 6-hex RGB of one colour-scheme slot, or `null` when the theme does not define it. */
2100
- color(slot) {
2101
- return parseClrScheme(this.#clrScheme()).get(slot) ?? null;
2102
- }
2103
- /** The theme's `a:fontScheme` (major/minor font families), or `null` when the theme declares none. */
2104
- get fontScheme() {
2105
- const themeElements = this.#themeElements();
2106
- const fontScheme = themeElements && firstChild(themeElements, "a:fontScheme");
2107
- if (!fontScheme) return null;
2108
- return {
2109
- name: attr(fontScheme, "name"),
2110
- major: readThemeFontFace(firstChild(fontScheme, "a:majorFont")),
2111
- minor: readThemeFontFace(firstChild(fontScheme, "a:minorFont"))
2112
- };
2113
- }
2114
- /** Escape hatch: the underlying `a:theme` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
2115
- get element_() {
2116
- return this.part.dom.documentElement;
2117
- }
2118
- /** Mark the theme part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
2119
- markDirty() {
2120
- this.part.markDirty();
2121
- }
2122
- #themeElements() {
2123
- const root = this.part.dom.documentElement;
2124
- return root ? firstChild(root, "a:themeElements") : null;
2125
- }
2126
- #clrScheme() {
2127
- const themeElements = this.#themeElements();
2128
- return themeElements ? firstChild(themeElements, "a:clrScheme") : null;
2129
- }
2130
- };
2131
- /** The placeholder shapes (`p:sp` carrying a `p:ph`) of a master/layout `p:spTree`, in document order. */
2132
- function placeholderShapes(spTree) {
2133
- if (!spTree) return [];
2134
- return getElements(spTree, "p:sp").filter((sp) => placeholderOf(sp) !== null);
2135
- }
2136
- /**
2137
- * One placeholder shape (`p:sp` with a `p:ph`) of a slide master or slide layout —
2138
- * the definition a slide's placeholder of the same `type`/`idx` inherits geometry
2139
- * and text style from. Geometry getters read the placeholder's *own* `a:xfrm`,
2140
- * which a master/layout placeholder carries explicitly (so it round-trips, unlike a
2141
- * notes placeholder's inherited geometry).
2142
- *
2143
- * This is the identity-and-geometry view. The same `p:sp` also appears in
2144
- * {@link SlideMaster.shapes} / {@link SlideLayout.shapes} as an `AutoShape`, which
2145
- * additionally carries the paint surface (`resolvedFill`, `resolvedLine`,
2146
- * `presetGeometry`, `rotation`, `absoluteFrame`) and reports its `p:ph` through
2147
- * `AutoShape.placeholder`. Read a placeholder here to *place* it; read it there to
2148
- * *draw* it.
2149
- */
2150
- var Placeholder = class {
2151
- sp;
2152
- part;
2153
- themeContext;
2154
- relationships;
2155
- constructor(sp, part, themeContext, relationships) {
2156
- this.sp = sp;
2157
- this.part = part;
2158
- this.themeContext = themeContext;
2159
- this.relationships = relationships;
2160
- }
2161
- #cNvPr() {
2162
- const nvSpPr = firstChild(this.sp, "p:nvSpPr");
2163
- return nvSpPr ? firstChild(nvSpPr, "p:cNvPr") : null;
2164
- }
2165
- /** Placeholder type (`p:ph/@type`: `title` | `body` | `sldNum` | …), or `null` when absent (a body placeholder). */
2166
- get type() {
2167
- const ph = placeholderOf(this.sp);
2168
- return ph ? attr(ph, "type") : null;
2169
- }
2170
- /** Placeholder index (`p:ph/@idx`), or `null` when unset. */
2171
- get idx() {
2172
- const ph = placeholderOf(this.sp);
2173
- return ph ? attr(ph, "idx") : null;
2174
- }
2175
- /** Shape name (`p:cNvPr/@name`), or `''` when unnamed. */
2176
- get name() {
2177
- const cNvPr = this.#cNvPr();
2178
- return (cNvPr && attr(cNvPr, "name")) ?? "";
2179
- }
2180
- /** Drawing id (`p:cNvPr/@id`), or `null` when absent. */
2181
- get id() {
2182
- const cNvPr = this.#cNvPr();
2183
- return cNvPr ? intValue(attr(cNvPr, "id")) : null;
2184
- }
2185
- /** Left edge in EMU (`a:off/@x`), or `null` when the placeholder carries no own `a:xfrm`. */
2186
- get left() {
2187
- return spPrXfrmEmu(this.sp, "a:off", "x");
2188
- }
2189
- /** Top edge in EMU (`a:off/@y`), or `null` when the placeholder carries no own `a:xfrm`. */
2190
- get top() {
2191
- return spPrXfrmEmu(this.sp, "a:off", "y");
2192
- }
2193
- /** Width in EMU (`a:ext/@cx`), or `null` when the placeholder carries no own `a:xfrm`. */
2194
- get width() {
2195
- return spPrXfrmEmu(this.sp, "a:ext", "cx");
2196
- }
2197
- /** Height in EMU (`a:ext/@cy`), or `null` when the placeholder carries no own `a:xfrm`. */
2198
- get height() {
2199
- return spPrXfrmEmu(this.sp, "a:ext", "cy");
2200
- }
2201
- /**
2202
- * The placeholder's text as a navigable {@link TextFrame} (`p:txBody`), or `null`
2203
- * when it carries no text body. Threaded with the owning master/layout theme
2204
- * context so a run's own `schemeClr` resolves through `Run.resolvedColor`.
2205
- */
2206
- get textFrame() {
2207
- const txBody = firstChild(this.sp, "p:txBody");
2208
- return txBody ? new TextFrame(txBody, this.part, this.themeContext, void 0, this.relationships) : null;
2209
- }
2210
- /** Escape hatch: the underlying `p:sp` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
2211
- get element_() {
2212
- return this.sp;
2213
- }
2214
- /** Mark the owning master/layout part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
2215
- markDirty() {
2216
- this.part.markDirty();
2217
- }
2218
- };
2219
- /**
2220
- * A deck's slide master (`slideMasterN.xml`) as a modeled object. Exposes the
2221
- * property tiers a bound slide inherits: the {@link colorMap} (token → theme slot),
2222
- * its {@link theme}, its {@link shapes} and the {@link placeholders} subset of them,
2223
- * its own {@link background}, and the {@link layouts} that build on it. Reachable
2224
- * from {@link SlideLayout.master} and `Slide.master`.
2225
- */
2226
- var SlideMaster = class {
2227
- opc;
2228
- part;
2229
- #themeContext;
2230
- constructor(opc, part) {
2231
- this.opc = opc;
2232
- this.part = part;
2233
- }
2234
- /** Partname of the master part. */
2235
- get partName() {
2236
- return this.part.partName;
2237
- }
2238
- /** This master part's relationships (its layouts, its theme, image embeds, …). */
2239
- get relationships() {
2240
- return this.opc.relationshipsFor(this.partName);
2241
- }
2242
- /** The master's authoring name (`p:cSld/@name`), or `''` when unnamed (the writer's default). */
2243
- get name() {
2244
- const cSld = this.#cSld();
2245
- return (cSld && attr(cSld, "name")) ?? "";
2246
- }
2247
- /** The master's theme (via its `theme` relationship), or `null` when absent. */
2248
- get theme() {
2249
- const themePart = this.#relTarget(THEME_REL);
2250
- return themePart ? new Theme(themePart) : null;
2251
- }
2252
- /**
2253
- * The master's colour map (`p:clrMap`): each of the 12 {@link ColorMapToken}s
2254
- * mapped to the {@link ThemeColorSlot} it resolves through. This is the indirection
2255
- * that lets a slide's `schemeClr val="tx1"` reach a theme slot (e.g. `tx1` → `dk1`).
2256
- * A token the map omits reads `null`.
2257
- */
2258
- get colorMap() {
2259
- const root = this.part.dom.documentElement;
2260
- const parsed = parseClrMap(root ? firstChild(root, "p:clrMap") : null);
2261
- const out = {};
2262
- for (const token of COLOR_MAP_TOKENS) out[token] = parsed.get(token) ?? null;
2263
- return out;
2264
- }
2265
- /**
2266
- * Every shape in the master's `p:cSld/p:spTree`, in document order — the same
2267
- * `AnyShape` union `Slide.shapes` returns, so shape-walking code applies
2268
- * unchanged. This is where a template's non-placeholder furniture lives: the
2269
- * header band, the rule under the title, the logo. Whether a given slide draws
2270
- * them is `Slide.showMasterSp` (and, for a layout, {@link SlideLayout.showMasterSp}).
2271
- *
2272
- * {@link placeholders} is the filtered view of the same tree.
2273
- */
2274
- get shapes() {
2275
- const spTree = this.#spTree();
2276
- return spTree ? buildShapes(spTree, this) : [];
2277
- }
2278
- /** The shape anywhere in the master's tree with the given drawing id, or `undefined`. */
2279
- shapeByIdDeep(id) {
2280
- return findShapeByIdDeep(this.shapes, id);
2281
- }
2282
- /** The master's placeholder shapes (`p:sp` carrying a `p:ph`), in document order. */
2283
- get placeholders() {
2284
- const ctx = this.themeContext();
2285
- const rels = this.relationships;
2286
- return placeholderShapes(this.#spTree()).map((sp) => new Placeholder(sp, this.part, ctx, rels));
2287
- }
2288
- /** The layouts built on this master (via `p:sldLayoutIdLst` → its relationships), in list order. */
2289
- get layouts() {
2290
- const root = this.part.dom.documentElement;
2291
- const lst = root && firstChild(root, "p:sldLayoutIdLst");
2292
- if (!lst) return [];
2293
- const rels = this.relationships;
2294
- const out = [];
2295
- for (const entry of getElements(lst, "p:sldLayoutId")) {
2296
- const relId = attr(entry, "r:id");
2297
- const part = relId ? this.opc.part(rels.resolveTarget(relId)) : null;
2298
- if (part) out.push(new SlideLayout(this.opc, part));
2299
- }
2300
- return out;
2301
- }
2302
- /**
2303
- * The master's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
2304
- * none. Scoped to the master's own element — a slide's *effective* background,
2305
- * resolved through the slide → layout → master chain, is `Slide.background`.
2306
- */
2307
- get background() {
2308
- const bg = backgroundElementOf(this.part.dom.documentElement);
2309
- if (!bg) return null;
2310
- const themePart = this.#relTarget(THEME_REL);
2311
- return readSlideBackground(bg, "master", this.themeContext(), this.relationships, themePart ? this.opc.relationshipsFor(themePart.partName) : null);
2312
- }
2313
- /**
2314
- * The master's colour/font context (its `p:clrMap` + theme), resolved once and
2315
- * cached. Backs each {@link Placeholder}'s text frame.
2316
- */
2317
- themeContext() {
2318
- return this.#themeContext ??= resolveMasterColorContext(this.opc, this.partName);
2319
- }
2320
- #relTarget(type) {
2321
- const rels = this.relationships;
2322
- const rel = rels.byType(type)[0];
2323
- return rel ? this.opc.part(rels.resolveTarget(rel.id)) ?? null : null;
2324
- }
2325
- #cSld() {
2326
- return cSldOf(this.part.dom.documentElement);
2327
- }
2328
- #spTree() {
2329
- return spTreeOf(this.part.dom.documentElement);
2330
- }
2331
- };
2332
- /**
2333
- * A deck's slide layout (`slideLayoutN.xml`) as a modeled object — the gallery entry
2334
- * a slide binds to. Exposes its {@link name}, its {@link type} (import-only; the
2335
- * writer authors none), its {@link master} (and, through it, the {@link theme}), its
2336
- * {@link shapes} and the {@link placeholders} subset of them, and its own
2337
- * {@link background}. Reachable from `Slide.layout` and {@link SlideMaster.layouts}.
2338
- */
2339
- var SlideLayout = class {
2340
- opc;
2341
- part;
2342
- #themeContext;
2343
- constructor(opc, part) {
2344
- this.opc = opc;
2345
- this.part = part;
2346
- }
2347
- /** Partname of the layout part. */
2348
- get partName() {
2349
- return this.part.partName;
2350
- }
2351
- /** This layout part's relationships (its master, image embeds, hyperlinks, …). */
2352
- get relationships() {
2353
- return this.opc.relationshipsFor(this.partName);
2354
- }
2355
- /** The layout's authoring name (`p:cSld/@name`, e.g. `Title and Content`), or `''` when unnamed. */
2356
- get name() {
2357
- const cSld = this.#cSld();
2358
- return (cSld && attr(cSld, "name")) ?? "";
2359
- }
2360
- /**
2361
- * The layout type (`p:sldLayout/@type`, e.g. `title` | `obj` | `blank`), or `null`
2362
- * when the layout declares none. Import-only: the writer authors no `@type`, so an
2363
- * authored deck reads `null`; an imported deck carries PowerPoint's value.
2364
- */
2365
- get type() {
2366
- const root = this.part.dom.documentElement;
2367
- return root ? attr(root, "type") : null;
2368
- }
2369
- /**
2370
- * Whether this layout draws the master's non-placeholder shapes
2371
- * (`p:sldLayout/@showMasterSp`). `xsd:boolean` defaulting to `true`, so an absent
2372
- * attribute means shown — the layout-tier counterpart of `Slide.showMasterSp`,
2373
- * and the one PowerPoint actually writes on a section-divider or full-bleed
2374
- * layout. A renderer resolving what to paint under a slide's own shapes has to
2375
- * consult both: the slide's flag, then its layout's.
2376
- */
2377
- get showMasterSp() {
2378
- const root = this.part.dom.documentElement;
2379
- return boolValue(root && attr(root, "showMasterSp")) !== false;
2380
- }
2381
- /** The master this layout is built on (via its `slideMaster` relationship), or `null` when absent. */
2382
- get master() {
2383
- const rels = this.relationships;
2384
- const rel = rels.byType(SLIDE_MASTER_REL)[0];
2385
- const part = rel ? this.opc.part(rels.resolveTarget(rel.id)) : null;
2386
- return part ? new SlideMaster(this.opc, part) : null;
2387
- }
2388
- /** The theme this layout resolves against, via its {@link master}, or `null`. */
2389
- get theme() {
2390
- return this.master?.theme ?? null;
2391
- }
2392
- /**
2393
- * Every shape in the layout's `p:cSld/p:spTree`, in document order — the same
2394
- * `AnyShape` union `Slide.shapes` returns, so shape-walking code applies
2395
- * unchanged. The write API authors non-placeholder shapes here: every
2396
- * `defineSlideMaster({ objects })` member that is not a `placeholder` (a `rect`,
2397
- * a `line`, an `image`, a `chart`, a `text` box) lands in this tree.
2398
- *
2399
- * {@link placeholders} is the filtered view of the same tree.
2400
- */
2401
- get shapes() {
2402
- const spTree = this.#spTree();
2403
- return spTree ? buildShapes(spTree, this) : [];
2404
- }
2405
- /** The shape anywhere in the layout's tree with the given drawing id, or `undefined`. */
2406
- shapeByIdDeep(id) {
2407
- return findShapeByIdDeep(this.shapes, id);
2408
- }
2409
- /** The layout's placeholder shapes (`p:sp` carrying a `p:ph`), in document order. */
2410
- get placeholders() {
2411
- const ctx = this.themeContext();
2412
- const rels = this.relationships;
2413
- return placeholderShapes(this.#spTree()).map((sp) => new Placeholder(sp, this.part, ctx, rels));
2414
- }
2415
- /**
2416
- * The layout's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
2417
- * none. A layout usually authors one — an explicit fill (`p:bgPr`) or a
2418
- * theme-indexed reference (`p:bgRef`). The slide's *effective* background is
2419
- * `Slide.background`.
2420
- */
2421
- get background() {
2422
- const bg = backgroundElementOf(this.part.dom.documentElement);
2423
- if (!bg) return null;
2424
- const themePart = this.master?.theme?.part ?? null;
2425
- return readSlideBackground(bg, "layout", this.themeContext(), this.relationships, themePart ? this.opc.relationshipsFor(themePart.partName) : null);
2426
- }
2427
- /**
2428
- * The layout's colour/font context (walked layout → master → theme), resolved once
2429
- * and cached. Backs each {@link Placeholder}'s text frame.
2430
- */
2431
- themeContext() {
2432
- return this.#themeContext ??= resolveLayoutColorContext(this.opc, this.partName);
2433
- }
2434
- #cSld() {
2435
- return cSldOf(this.part.dom.documentElement);
2436
- }
2437
- #spTree() {
2438
- return spTreeOf(this.part.dom.documentElement);
2439
- }
2440
- };
2441
- //#endregion
2442
2490
  //#region src/read/api/transition.ts
2443
2491
  /**
2444
2492
  * Typed read/edit model for a slide's `p:transition` (slide-show transition).
@@ -4299,7 +4347,7 @@ function importSlideRebind(dest, ctx, source, sourceSlide, carryGraphics) {
4299
4347
  targetRels.addWithId(rel.id, rel.type, rel.target, "External");
4300
4348
  continue;
4301
4349
  }
4302
- const newTarget = copyPart(ctx, sourceRels.resolveTarget(rel.id), isSharedByPageCopies(rel.type) ? void 0 : owned);
4350
+ const newTarget = copyPart(ctx, sourceRels.resolveTarget(rel.id), isSharedByPageCopies(rel.type) ? void 0 : owned, true);
4303
4351
  targetRels.addWithId(rel.id, rel.type, relativePartName(newPartName, newTarget));
4304
4352
  }
4305
4353
  if (carryGraphics) carryMasterGraphics(dest, ctx, slideRoot, newPartName, sourceSlide.partName, owned);
@@ -4487,7 +4535,6 @@ const PACKAGE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relat
4487
4535
  const AUDIO_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio";
4488
4536
  const VIDEO_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video";
4489
4537
  const MS_MEDIA_REL = "http://schemas.microsoft.com/office/2007/relationships/media";
4490
- const SLIDE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.slide+xml";
4491
4538
  const CHART_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml";
4492
4539
  const CHARTEX_CONTENT_TYPE = "application/vnd.ms-office.chartex+xml";
4493
4540
  const CHART_STYLE_CONTENT_TYPE = "application/vnd.ms-office.chartstyle+xml";
@@ -5375,6 +5422,6 @@ var Presentation = class Presentation {
5375
5422
  }
5376
5423
  };
5377
5424
  //#endregion
5378
- export { Placeholder as a, Theme as c, NotesSlide as d, OpcPackage as f, ContentTypes as h, Slide as i, readTagsForPart as l, Part as m, readCoreProperties as n, SlideLayout as o, Relationships as p, readCustomProperties as r, SlideMaster as s, Presentation as t, NotesPlaceholder as u };
5425
+ export { readTagsForPart as a, Placeholder as c, Theme as d, OpcPackage as f, ContentTypes as h, Slide as i, SlideLayout as l, Part as m, readCoreProperties as n, NotesPlaceholder as o, Relationships as p, readCustomProperties as r, NotesSlide as s, Presentation as t, SlideMaster as u };
5379
5426
 
5380
- //# sourceMappingURL=presentation-3rAKKRfm.js.map
5427
+ //# sourceMappingURL=presentation-Cp7vzlAw.js.map