@office-open/pptx 0.4.0 → 0.4.2

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/index.mjs CHANGED
@@ -6,6 +6,92 @@ import { attr, attrNum, attrs, escapeXml, findChild, xml } from "@office-open/xm
6
6
  import { DEFAULT_DRAWING_XML, SmartArtCollection, createDataModel, getColorXml, getLayoutXml, getStyleXml } from "@office-open/core/smartart";
7
7
  import { Zip, ZipDeflate, ZipPassThrough, zipSync } from "fflate";
8
8
  export * from "@office-open/core/values";
9
+ //#region src/file/comment/comment-author-list.ts
10
+ /**
11
+ * p:cmAuthorLst — Comment author list stored in `ppt/commentAuthors.xml`.
12
+ */
13
+ var CommentAuthorList = class extends XmlComponent {
14
+ constructor(authors) {
15
+ super("p:cmAuthorLst");
16
+ this.root.push(new NextAttributeComponent({ "xmlns:p": {
17
+ key: "xmlns:p",
18
+ value: "http://schemas.openxmlformats.org/presentationml/2006/main"
19
+ } }));
20
+ for (const author of authors) this.root.push(new BuilderElement({
21
+ name: "p:cmAuthor",
22
+ attributes: {
23
+ id: {
24
+ key: "id",
25
+ value: author.id
26
+ },
27
+ name: {
28
+ key: "name",
29
+ value: author.name
30
+ },
31
+ initials: {
32
+ key: "initials",
33
+ value: author.initials
34
+ },
35
+ clrIdx: {
36
+ key: "clrIdx",
37
+ value: author.clrIdx
38
+ },
39
+ lastIdx: {
40
+ key: "lastIdx",
41
+ value: author.lastIdx
42
+ }
43
+ }
44
+ }));
45
+ }
46
+ };
47
+ //#endregion
48
+ //#region src/file/comment/slide-comment-list.ts
49
+ /**
50
+ * p:cmLst — Comment list stored in `ppt/comments/slideN.xml`.
51
+ */
52
+ var SlideCommentList = class extends XmlComponent {
53
+ constructor(comments) {
54
+ super("p:cmLst");
55
+ this.root.push(new NextAttributeComponent({ "xmlns:p": {
56
+ key: "xmlns:p",
57
+ value: "http://schemas.openxmlformats.org/presentationml/2006/main"
58
+ } }));
59
+ for (const comment of comments) this.root.push(new BuilderElement({
60
+ name: "p:cm",
61
+ attributes: {
62
+ authorId: {
63
+ key: "authorId",
64
+ value: comment.authorId
65
+ },
66
+ idx: {
67
+ key: "idx",
68
+ value: comment.idx
69
+ },
70
+ ...comment.date != null ? { dt: {
71
+ key: "dt",
72
+ value: comment.date
73
+ } } : {}
74
+ },
75
+ children: [new BuilderElement({
76
+ name: "p:pos",
77
+ attributes: {
78
+ x: {
79
+ key: "x",
80
+ value: comment.x
81
+ },
82
+ y: {
83
+ key: "y",
84
+ value: comment.y
85
+ }
86
+ }
87
+ }), new BuilderElement({
88
+ name: "p:text",
89
+ children: [comment.text]
90
+ })]
91
+ }));
92
+ }
93
+ };
94
+ //#endregion
9
95
  //#region src/file/content-types/content-types.ts
10
96
  /**
11
97
  * Content Types module for PPTX packages.
@@ -18,6 +104,8 @@ const PPTX_SLIDE_MASTER = "application/vnd.openxmlformats-officedocument.present
18
104
  const PPTX_SLIDE_LAYOUT = "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml";
19
105
  const PPTX_THEME = "application/vnd.openxmlformats-officedocument.theme+xml";
20
106
  const PPTX_NOTES = "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml";
107
+ const PPTX_COMMENTS = "application/vnd.openxmlformats-officedocument.presentationml.comments+xml";
108
+ const PPTX_COMMENT_AUTHORS = "application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml";
21
109
  const PPTX_CHART = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml";
22
110
  const STATIC_CHILDREN = [{ _attr: { xmlns: "http://schemas.openxmlformats.org/package/2006/content-types" } }, ...[
23
111
  {
@@ -119,6 +207,20 @@ var ContentTypes = class extends BaseXmlComponent {
119
207
  key: `/ppt/notesSlides/notesSlide${index}.xml`
120
208
  });
121
209
  }
210
+ addComments(index) {
211
+ this.dynamicEntries.push({
212
+ type: "Override",
213
+ contentType: PPTX_COMMENTS,
214
+ key: `/ppt/comments/comment${index}.xml`
215
+ });
216
+ }
217
+ addCommentAuthors() {
218
+ this.dynamicEntries.push({
219
+ type: "Override",
220
+ contentType: PPTX_COMMENT_AUTHORS,
221
+ key: "/ppt/commentAuthors.xml"
222
+ });
223
+ }
122
224
  addChart(index) {
123
225
  this.dynamicEntries.push({
124
226
  type: "Override",
@@ -1373,7 +1475,7 @@ var DefaultSlideMaster = class DefaultSlideMaster extends ImportedXmlComponent {
1373
1475
  };
1374
1476
  //#endregion
1375
1477
  //#region src/file/animation/timing.ts
1376
- const PRESET_IDS = {
1478
+ const ENTR_PRESET_IDS = {
1377
1479
  appear: 1,
1378
1480
  fade: 10,
1379
1481
  fly: 2,
@@ -1389,6 +1491,42 @@ const PRESET_IDS = {
1389
1491
  push: 19,
1390
1492
  strips: 23
1391
1493
  };
1494
+ const EXIT_PRESET_IDS = {
1495
+ appear: 53,
1496
+ fade: 59,
1497
+ fly: 51,
1498
+ wipe: 72,
1499
+ dissolve: 84,
1500
+ split: 71,
1501
+ blinds: 75,
1502
+ checker: 76,
1503
+ randomBars: 74,
1504
+ wheel: 77,
1505
+ zoom: 60,
1506
+ cover: 78,
1507
+ push: 69,
1508
+ strips: 73
1509
+ };
1510
+ const EMPH_PRESET_IDS = {
1511
+ growShrink: 53,
1512
+ spin: 54,
1513
+ growWithTurn: 56,
1514
+ colorChange: 29,
1515
+ transparency: 57,
1516
+ boldFlash: 50,
1517
+ wave: 55,
1518
+ pulse: 58
1519
+ };
1520
+ const PATH_PRESET_IDS = {
1521
+ customPath: 200,
1522
+ arc: 201,
1523
+ bounce: 202,
1524
+ circle: 203,
1525
+ curve: 204,
1526
+ figureEight: 205,
1527
+ line: 206,
1528
+ loop: 207
1529
+ };
1392
1530
  const DIRECTION_SUBTYPES = {
1393
1531
  left: 4,
1394
1532
  right: 8,
@@ -1461,169 +1599,522 @@ const DIRECTION_FILTER = {
1461
1599
  },
1462
1600
  wheel: {}
1463
1601
  };
1464
- /**
1465
- * p:timing — Slide timing for shape animations.
1466
- */
1467
- var SlideTiming = class extends XmlComponent {
1468
- constructor(entries) {
1469
- super("p:timing");
1470
- if (entries.length === 0) return;
1471
- let id = 1;
1472
- const rootCtnId = id++;
1473
- const seqCtnId = id++;
1474
- const animationNodes = [];
1475
- let clickGroupDelay = 0;
1476
- for (let i = 0; i < entries.length; i++) {
1477
- const { spid, options } = entries[i];
1478
- const nodeType = options.trigger === "withPrevious" ? "withEffect" : options.trigger === "afterPrevious" ? "afterEffect" : "clickEffect";
1479
- if (options.trigger === "afterPrevious" && i > 0) clickGroupDelay += (options.duration ?? 500) + (options.delay ?? 0);
1480
- if (options.trigger === "onClick" || options.trigger === void 0) clickGroupDelay = 0;
1481
- const groupCtnId = id++;
1482
- const effectCtnId = id++;
1483
- const setCtnId = id++;
1484
- const effectCtnId2 = id++;
1485
- const presetId = PRESET_IDS[options.type];
1486
- const presetSubtype = options.direction ? DIRECTION_SUBTYPES[options.direction] ?? 0 : 0;
1487
- const effectChildren = [];
1488
- effectChildren.push(new BuilderElement({
1489
- name: "p:set",
1602
+ const PATH_STRINGS = {
1603
+ customPath: "",
1604
+ arc: "M 0 0 C 50 -100 100 -100 150 0",
1605
+ bounce: "M 0 0 L 50 0 C 50 -30 30 -50 0 -50 C -30 -50 -50 -30 -50 0 L 50 0",
1606
+ circle: "M 0 0 C 50 -50 100 0 50 50 C 0 100 -50 50 0 0",
1607
+ curve: "M 0 0 C 50 -80 100 -80 150 0 C 200 80 250 80 300 0",
1608
+ figureEight: "M 0 0 C 50 -50 100 0 50 50 C 0 100 -50 50 0 0 C -50 -50 -100 0 -50 50 C 0 100 50 50 0 0",
1609
+ line: "M 0 0 L 200 0",
1610
+ loop: "M 0 0 C 50 -80 100 0 50 80 C 0 160 -50 80 0 0"
1611
+ };
1612
+ function resolvePresetId(options) {
1613
+ const cls = options.class ?? "entr";
1614
+ if (cls === "emph" && options.emphasisType) return EMPH_PRESET_IDS[options.emphasisType] ?? 0;
1615
+ if (options.pathType) return PATH_PRESET_IDS[options.pathType] ?? 200;
1616
+ const type = options.type ?? "appear";
1617
+ return (cls === "exit" ? EXIT_PRESET_IDS : ENTR_PRESET_IDS)[type] ?? 0;
1618
+ }
1619
+ function resolvePresetClass(options) {
1620
+ if (options.pathType) return "emph";
1621
+ return options.class ?? "entr";
1622
+ }
1623
+ function resolvePresetSubtype(options) {
1624
+ if (options.direction) return DIRECTION_SUBTYPES[options.direction] ?? 0;
1625
+ return 0;
1626
+ }
1627
+ function buildEntrOrExitEffects(options, spid, ids) {
1628
+ const children = [];
1629
+ const cls = options.class ?? "entr";
1630
+ const visibility = cls === "exit" ? "hidden" : "visible";
1631
+ children.push(new BuilderElement({
1632
+ name: "p:set",
1633
+ children: [new BuilderElement({
1634
+ name: "p:cBhvr",
1635
+ children: [
1636
+ new BuilderElement({
1637
+ name: "p:cTn",
1638
+ attributes: {
1639
+ id: {
1640
+ key: "id",
1641
+ value: ids.set
1642
+ },
1643
+ dur: {
1644
+ key: "dur",
1645
+ value: "1"
1646
+ },
1647
+ fill: {
1648
+ key: "fill",
1649
+ value: "hold"
1650
+ }
1651
+ },
1652
+ children: [new BuilderElement({
1653
+ name: "p:stCondLst",
1654
+ children: [new BuilderElement({
1655
+ name: "p:cond",
1656
+ attributes: { delay: {
1657
+ key: "delay",
1658
+ value: "0"
1659
+ } }
1660
+ })]
1661
+ })]
1662
+ }),
1663
+ new BuilderElement({
1664
+ name: "p:tgtEl",
1665
+ children: [new BuilderElement({
1666
+ name: "p:spTgt",
1667
+ attributes: { spid: {
1668
+ key: "spid",
1669
+ value: spid
1670
+ } }
1671
+ })]
1672
+ }),
1673
+ new BuilderElement({
1674
+ name: "p:attrNameLst",
1675
+ children: [new StringContainer("p:attrName", "style.visibility")]
1676
+ })
1677
+ ]
1678
+ }), new BuilderElement({
1679
+ name: "p:to",
1680
+ children: [new BuilderElement({
1681
+ name: "p:strVal",
1682
+ attributes: { val: {
1683
+ key: "val",
1684
+ value: visibility
1685
+ } }
1686
+ })]
1687
+ })]
1688
+ }));
1689
+ if (options.type !== "appear") {
1690
+ const filterBase = FILTER_MAP[options.type ?? "fade"];
1691
+ const dirMap = DIRECTION_FILTER[options.type ?? "fade"];
1692
+ const dirFilter = options.direction && dirMap ? dirMap[options.direction] : void 0;
1693
+ let filter = filterBase;
1694
+ if (options.type === "wheel") filter = "wheel(4)";
1695
+ else if (options.type === "split") filter = `split(${options.direction ?? "horizontal"})`;
1696
+ else if (dirFilter) filter = `${filterBase}(${dirFilter})`;
1697
+ const transition = cls === "exit" ? "out" : "in";
1698
+ children.push(new BuilderElement({
1699
+ name: "p:animEffect",
1700
+ attributes: {
1701
+ transition: {
1702
+ key: "transition",
1703
+ value: transition
1704
+ },
1705
+ filter: {
1706
+ key: "filter",
1707
+ value: filter
1708
+ }
1709
+ },
1710
+ children: [new BuilderElement({
1711
+ name: "p:cBhvr",
1712
+ children: [new BuilderElement({
1713
+ name: "p:cTn",
1714
+ attributes: {
1715
+ id: {
1716
+ key: "id",
1717
+ value: ids.effect
1718
+ },
1719
+ dur: {
1720
+ key: "dur",
1721
+ value: String(options.duration ?? 500)
1722
+ },
1723
+ fill: {
1724
+ key: "fill",
1725
+ value: "hold"
1726
+ }
1727
+ }
1728
+ }), new BuilderElement({
1729
+ name: "p:tgtEl",
1730
+ children: [new BuilderElement({
1731
+ name: "p:spTgt",
1732
+ attributes: { spid: {
1733
+ key: "spid",
1734
+ value: spid
1735
+ } }
1736
+ })]
1737
+ })]
1738
+ })]
1739
+ }));
1740
+ }
1741
+ return children;
1742
+ }
1743
+ function buildEmphasisEffects(options, spid, ids) {
1744
+ const emphType = options.emphasisType ?? "growShrink";
1745
+ const children = [];
1746
+ const dur = String(options.duration ?? 500);
1747
+ const tgtEl = new BuilderElement({
1748
+ name: "p:tgtEl",
1749
+ children: [new BuilderElement({
1750
+ name: "p:spTgt",
1751
+ attributes: { spid: {
1752
+ key: "spid",
1753
+ value: spid
1754
+ } }
1755
+ })]
1756
+ });
1757
+ switch (emphType) {
1758
+ case "growShrink":
1759
+ children.push(new BuilderElement({
1760
+ name: "p:animScale",
1490
1761
  children: [new BuilderElement({
1491
1762
  name: "p:cBhvr",
1492
- children: [
1493
- new BuilderElement({
1494
- name: "p:cTn",
1495
- attributes: {
1496
- id: {
1497
- key: "id",
1498
- value: setCtnId
1499
- },
1500
- dur: {
1501
- key: "dur",
1502
- value: "1"
1503
- },
1504
- fill: {
1505
- key: "fill",
1506
- value: "hold"
1507
- }
1763
+ children: [new BuilderElement({
1764
+ name: "p:cTn",
1765
+ attributes: {
1766
+ id: {
1767
+ key: "id",
1768
+ value: ids.effect
1769
+ },
1770
+ dur: {
1771
+ key: "dur",
1772
+ value: dur
1773
+ },
1774
+ fill: {
1775
+ key: "fill",
1776
+ value: "hold"
1508
1777
  },
1778
+ ...options.autoReverse ? { autoRev: {
1779
+ key: "autoRev",
1780
+ value: 1
1781
+ } } : {}
1782
+ },
1783
+ children: [new BuilderElement({
1784
+ name: "p:stCondLst",
1509
1785
  children: [new BuilderElement({
1510
- name: "p:stCondLst",
1511
- children: [new BuilderElement({
1512
- name: "p:cond",
1513
- attributes: { delay: {
1514
- key: "delay",
1515
- value: "0"
1516
- } }
1517
- })]
1786
+ name: "p:cond",
1787
+ attributes: { delay: {
1788
+ key: "delay",
1789
+ value: "0"
1790
+ } }
1518
1791
  })]
1519
- }),
1520
- new BuilderElement({
1521
- name: "p:tgtEl",
1792
+ })]
1793
+ }), tgtEl]
1794
+ }), new BuilderElement({
1795
+ name: "p:by",
1796
+ attributes: {
1797
+ x: {
1798
+ key: "x",
1799
+ value: "150000"
1800
+ },
1801
+ y: {
1802
+ key: "y",
1803
+ value: "150000"
1804
+ }
1805
+ }
1806
+ })]
1807
+ }));
1808
+ break;
1809
+ case "spin":
1810
+ children.push(new BuilderElement({
1811
+ name: "p:animRot",
1812
+ attributes: { by: {
1813
+ key: "by",
1814
+ value: "3600000"
1815
+ } },
1816
+ children: [new BuilderElement({
1817
+ name: "p:cBhvr",
1818
+ children: [new BuilderElement({
1819
+ name: "p:cTn",
1820
+ attributes: {
1821
+ id: {
1822
+ key: "id",
1823
+ value: ids.effect
1824
+ },
1825
+ dur: {
1826
+ key: "dur",
1827
+ value: dur
1828
+ },
1829
+ fill: {
1830
+ key: "fill",
1831
+ value: "hold"
1832
+ }
1833
+ },
1834
+ children: [new BuilderElement({
1835
+ name: "p:stCondLst",
1522
1836
  children: [new BuilderElement({
1523
- name: "p:spTgt",
1524
- attributes: { spid: {
1525
- key: "spid",
1526
- value: spid
1837
+ name: "p:cond",
1838
+ attributes: { delay: {
1839
+ key: "delay",
1840
+ value: "0"
1527
1841
  } }
1528
1842
  })]
1529
- }),
1530
- new BuilderElement({
1531
- name: "p:attrNameLst",
1532
- children: [new StringContainer("p:attrName", "style.visibility")]
1533
- })
1534
- ]
1843
+ })]
1844
+ }), tgtEl]
1845
+ })]
1846
+ }));
1847
+ break;
1848
+ case "colorChange": {
1849
+ const color = options.color ?? "FF0000";
1850
+ children.push(new BuilderElement({
1851
+ name: "p:animClr",
1852
+ children: [new BuilderElement({
1853
+ name: "p:cBhvr",
1854
+ children: [new BuilderElement({
1855
+ name: "p:cTn",
1856
+ attributes: {
1857
+ id: {
1858
+ key: "id",
1859
+ value: ids.effect
1860
+ },
1861
+ dur: {
1862
+ key: "dur",
1863
+ value: dur
1864
+ },
1865
+ fill: {
1866
+ key: "fill",
1867
+ value: "hold"
1868
+ }
1869
+ },
1870
+ children: [new BuilderElement({
1871
+ name: "p:stCondLst",
1872
+ children: [new BuilderElement({
1873
+ name: "p:cond",
1874
+ attributes: { delay: {
1875
+ key: "delay",
1876
+ value: "0"
1877
+ } }
1878
+ })]
1879
+ })]
1880
+ }), tgtEl]
1535
1881
  }), new BuilderElement({
1536
1882
  name: "p:to",
1537
1883
  children: [new BuilderElement({
1538
- name: "p:strVal",
1884
+ name: "a:srgbClr",
1539
1885
  attributes: { val: {
1540
1886
  key: "val",
1541
- value: "visible"
1887
+ value: color
1542
1888
  } }
1543
1889
  })]
1544
1890
  })]
1545
1891
  }));
1546
- if (options.type !== "appear") {
1547
- const filterBase = FILTER_MAP[options.type];
1548
- const dirMap = DIRECTION_FILTER[options.type];
1549
- const dirFilter = options.direction && dirMap ? dirMap[options.direction] : void 0;
1550
- let filter = filterBase;
1551
- if (options.type === "wheel") filter = "wheel(4)";
1552
- else if (options.type === "split") filter = `split(${options.direction ?? "horizontal"})`;
1553
- else if (dirFilter) filter = `${filterBase}(${dirFilter})`;
1554
- effectChildren.push(new BuilderElement({
1555
- name: "p:animEffect",
1556
- attributes: {
1557
- transition: {
1558
- key: "transition",
1559
- value: "in"
1560
- },
1561
- filter: {
1562
- key: "filter",
1563
- value: filter
1564
- }
1892
+ break;
1893
+ }
1894
+ case "transparency":
1895
+ children.push(new BuilderElement({
1896
+ name: "p:anim",
1897
+ attributes: {
1898
+ calcmode: {
1899
+ key: "calcmode",
1900
+ value: "lin"
1565
1901
  },
1566
- children: [new BuilderElement({
1567
- name: "p:cBhvr",
1568
- children: [new BuilderElement({
1902
+ valueType: {
1903
+ key: "valueType",
1904
+ value: "num"
1905
+ },
1906
+ from: {
1907
+ key: "from",
1908
+ value: "0"
1909
+ },
1910
+ to: {
1911
+ key: "to",
1912
+ value: "1"
1913
+ }
1914
+ },
1915
+ children: [new BuilderElement({
1916
+ name: "p:cBhvr",
1917
+ children: [
1918
+ new BuilderElement({
1569
1919
  name: "p:cTn",
1570
1920
  attributes: {
1571
1921
  id: {
1572
1922
  key: "id",
1573
- value: effectCtnId2
1923
+ value: ids.effect
1574
1924
  },
1575
1925
  dur: {
1576
1926
  key: "dur",
1577
- value: String(options.duration ?? 500)
1927
+ value: dur
1578
1928
  },
1579
1929
  fill: {
1580
1930
  key: "fill",
1581
1931
  value: "hold"
1582
1932
  }
1583
1933
  }
1584
- }), new BuilderElement({
1585
- name: "p:tgtEl",
1586
- children: [new BuilderElement({
1587
- name: "p:spTgt",
1588
- attributes: { spid: {
1589
- key: "spid",
1590
- value: spid
1591
- } }
1592
- })]
1593
- })]
1594
- })]
1595
- }));
1934
+ }),
1935
+ tgtEl,
1936
+ new BuilderElement({
1937
+ name: "p:attrNameLst",
1938
+ children: [new StringContainer("p:attrName", "style.opacity")]
1939
+ })
1940
+ ]
1941
+ })]
1942
+ }));
1943
+ break;
1944
+ default:
1945
+ children.push(new BuilderElement({
1946
+ name: "p:animEffect",
1947
+ attributes: {
1948
+ transition: {
1949
+ key: "transition",
1950
+ value: "in"
1951
+ },
1952
+ filter: {
1953
+ key: "filter",
1954
+ value: emphType
1955
+ }
1956
+ },
1957
+ children: [new BuilderElement({
1958
+ name: "p:cBhvr",
1959
+ children: [new BuilderElement({
1960
+ name: "p:cTn",
1961
+ attributes: {
1962
+ id: {
1963
+ key: "id",
1964
+ value: ids.effect
1965
+ },
1966
+ dur: {
1967
+ key: "dur",
1968
+ value: dur
1969
+ },
1970
+ fill: {
1971
+ key: "fill",
1972
+ value: "hold"
1973
+ }
1974
+ }
1975
+ }), tgtEl]
1976
+ })]
1977
+ }));
1978
+ break;
1979
+ }
1980
+ return children;
1981
+ }
1982
+ function buildPathEffects(options, spid, ids) {
1983
+ const pathStr = options.path ?? PATH_STRINGS[options.pathType ?? "customPath"] ?? "";
1984
+ const dur = String(options.duration ?? 1e3);
1985
+ return [new BuilderElement({
1986
+ name: "p:animMotion",
1987
+ attributes: {
1988
+ origin: {
1989
+ key: "origin",
1990
+ value: "layout"
1991
+ },
1992
+ path: {
1993
+ key: "path",
1994
+ value: pathStr
1596
1995
  }
1996
+ },
1997
+ children: [new BuilderElement({
1998
+ name: "p:cBhvr",
1999
+ children: [new BuilderElement({
2000
+ name: "p:cTn",
2001
+ attributes: {
2002
+ id: {
2003
+ key: "id",
2004
+ value: ids.effect
2005
+ },
2006
+ dur: {
2007
+ key: "dur",
2008
+ value: dur
2009
+ },
2010
+ fill: {
2011
+ key: "fill",
2012
+ value: "hold"
2013
+ }
2014
+ },
2015
+ children: [new BuilderElement({
2016
+ name: "p:stCondLst",
2017
+ children: [new BuilderElement({
2018
+ name: "p:cond",
2019
+ attributes: { delay: {
2020
+ key: "delay",
2021
+ value: "0"
2022
+ } }
2023
+ })]
2024
+ })]
2025
+ }), new BuilderElement({
2026
+ name: "p:tgtEl",
2027
+ children: [new BuilderElement({
2028
+ name: "p:spTgt",
2029
+ attributes: { spid: {
2030
+ key: "spid",
2031
+ value: spid
2032
+ } }
2033
+ })]
2034
+ })]
2035
+ })]
2036
+ })];
2037
+ }
2038
+ /**
2039
+ * p:timing — Slide timing for shape animations.
2040
+ */
2041
+ var SlideTiming = class extends XmlComponent {
2042
+ constructor(entries) {
2043
+ super("p:timing");
2044
+ if (entries.length === 0) return;
2045
+ let id = 1;
2046
+ const rootCtnId = id++;
2047
+ const seqCtnId = id++;
2048
+ const animationNodes = [];
2049
+ let clickGroupDelay = 0;
2050
+ for (let i = 0; i < entries.length; i++) {
2051
+ const { spid, options } = entries[i];
2052
+ const nodeType = options.trigger === "withPrevious" ? "withEffect" : options.trigger === "afterPrevious" ? "afterEffect" : "clickEffect";
2053
+ if (options.trigger === "afterPrevious" && i > 0) clickGroupDelay += (options.duration ?? 500) + (options.delay ?? 0);
2054
+ if (options.trigger === "onClick" || options.trigger === void 0) clickGroupDelay = 0;
2055
+ const groupCtnId = id++;
2056
+ const effectCtnId = id++;
2057
+ const setCtnId = id++;
2058
+ const effectCtnId2 = id++;
2059
+ const presetId = resolvePresetId(options);
2060
+ const presetClass = resolvePresetClass(options);
2061
+ const presetSubtype = resolvePresetSubtype(options);
2062
+ let effectChildren;
2063
+ if (options.pathType) effectChildren = buildPathEffects(options, spid, {
2064
+ set: setCtnId,
2065
+ effect: effectCtnId2
2066
+ });
2067
+ else if (presetClass === "emph") effectChildren = buildEmphasisEffects(options, spid, {
2068
+ set: setCtnId,
2069
+ effect: effectCtnId2
2070
+ });
2071
+ else effectChildren = buildEntrOrExitEffects(options, spid, {
2072
+ set: setCtnId,
2073
+ effect: effectCtnId2
2074
+ });
2075
+ const cTnAttrs = {
2076
+ id: {
2077
+ key: "id",
2078
+ value: effectCtnId
2079
+ },
2080
+ presetID: {
2081
+ key: "presetID",
2082
+ value: presetId
2083
+ },
2084
+ presetClass: {
2085
+ key: "presetClass",
2086
+ value: presetClass
2087
+ },
2088
+ presetSubtype: {
2089
+ key: "presetSubtype",
2090
+ value: presetSubtype
2091
+ },
2092
+ fill: {
2093
+ key: "fill",
2094
+ value: "hold"
2095
+ },
2096
+ nodeType: {
2097
+ key: "nodeType",
2098
+ value: nodeType
2099
+ }
2100
+ };
2101
+ if (options.speed !== void 0) cTnAttrs.spd = {
2102
+ key: "spd",
2103
+ value: String(options.speed)
2104
+ };
2105
+ if (options.repeatCount !== void 0) cTnAttrs.repeatCount = {
2106
+ key: "repeatCount",
2107
+ value: String(options.repeatCount)
2108
+ };
2109
+ if (options.autoReverse) cTnAttrs.autoRev = {
2110
+ key: "autoRev",
2111
+ value: 1
2112
+ };
1597
2113
  const innerPar = new BuilderElement({
1598
2114
  name: "p:par",
1599
2115
  children: [new BuilderElement({
1600
2116
  name: "p:cTn",
1601
- attributes: {
1602
- id: {
1603
- key: "id",
1604
- value: effectCtnId
1605
- },
1606
- presetID: {
1607
- key: "presetID",
1608
- value: presetId
1609
- },
1610
- presetClass: {
1611
- key: "presetClass",
1612
- value: "entr"
1613
- },
1614
- presetSubtype: {
1615
- key: "presetSubtype",
1616
- value: presetSubtype
1617
- },
1618
- fill: {
1619
- key: "fill",
1620
- value: "hold"
1621
- },
1622
- nodeType: {
1623
- key: "nodeType",
1624
- value: nodeType
1625
- }
1626
- },
2117
+ attributes: cTnAttrs,
1627
2118
  children: [new BuilderElement({
1628
2119
  name: "p:stCondLst",
1629
2120
  children: [new BuilderElement({
@@ -2434,6 +2925,10 @@ function buildRelationships(entries) {
2434
2925
  for (const e of entries) rels.addRelationship(e.id, e.type, e.target, e.mode);
2435
2926
  return rels;
2436
2927
  }
2928
+ function deriveInitials(name) {
2929
+ const parts = name.trim().split(/\s+/);
2930
+ return parts.length >= 2 ? (parts[0][0] + parts[parts.length - 1][0]).toUpperCase() : name.slice(0, 2).toUpperCase();
2931
+ }
2437
2932
  var File = class {
2438
2933
  slideOptions;
2439
2934
  corePropsOptions;
@@ -2460,6 +2955,8 @@ var File = class {
2460
2955
  slides;
2461
2956
  slideWrappers;
2462
2957
  notesSlides;
2958
+ commentAuthorList;
2959
+ slideCommentLists;
2463
2960
  fileRels;
2464
2961
  constructor(options) {
2465
2962
  const slides = options.slides ?? [];
@@ -2478,10 +2975,16 @@ var File = class {
2478
2975
  get ContentTypes() {
2479
2976
  if (!this.contentTypes) {
2480
2977
  this.contentTypes = new ContentTypes();
2978
+ let hasComments = false;
2481
2979
  for (let i = 0; i < this.slideOptions.length; i++) {
2482
2980
  this.contentTypes.addSlide(i + 1);
2483
2981
  if (this.slideOptions[i].notes) this.contentTypes.addNotesSlide(i + 1);
2982
+ if (this.slideOptions[i].comments && this.slideOptions[i].comments.length > 0) {
2983
+ this.contentTypes.addComments(i + 1);
2984
+ hasComments = true;
2985
+ }
2484
2986
  }
2987
+ if (hasComments) this.contentTypes.addCommentAuthors();
2485
2988
  }
2486
2989
  return this.contentTypes;
2487
2990
  }
@@ -2608,6 +3111,58 @@ var File = class {
2608
3111
  get NotesMasterRelationships() {
2609
3112
  return this.notesMasterRels ??= new Relationships();
2610
3113
  }
3114
+ get CommentAuthorList() {
3115
+ if (!this.commentAuthorList && !this.slideCommentLists) this.buildComments();
3116
+ return this.commentAuthorList;
3117
+ }
3118
+ get SlideCommentLists() {
3119
+ if (!this.slideCommentLists) this.buildComments();
3120
+ return this.slideCommentLists;
3121
+ }
3122
+ buildComments() {
3123
+ const authorMap = /* @__PURE__ */ new Map();
3124
+ let nextAuthorId = 0;
3125
+ this.slideCommentLists = Array.from({ length: this.slideOptions.length });
3126
+ for (let i = 0; i < this.slideOptions.length; i++) {
3127
+ const slideComments = this.slideOptions[i].comments;
3128
+ if (!slideComments || slideComments.length === 0) continue;
3129
+ const commentEntries = [];
3130
+ for (const c of slideComments) {
3131
+ let author = authorMap.get(c.author);
3132
+ if (!author) {
3133
+ const id = nextAuthorId++;
3134
+ author = {
3135
+ id,
3136
+ name: c.author,
3137
+ initials: c.initials || deriveInitials(c.author),
3138
+ clrIdx: id,
3139
+ commentCount: 0
3140
+ };
3141
+ authorMap.set(c.author, author);
3142
+ }
3143
+ author.commentCount++;
3144
+ commentEntries.push({
3145
+ authorId: author.id,
3146
+ idx: author.commentCount,
3147
+ date: c.date,
3148
+ x: c.x,
3149
+ y: c.y,
3150
+ text: c.text
3151
+ });
3152
+ }
3153
+ this.slideCommentLists[i] = new SlideCommentList(commentEntries);
3154
+ }
3155
+ if (authorMap.size > 0) {
3156
+ const authors = [...authorMap.values()].map((a) => ({
3157
+ id: a.id,
3158
+ name: a.name,
3159
+ initials: a.initials,
3160
+ clrIdx: a.clrIdx,
3161
+ lastIdx: a.commentCount
3162
+ }));
3163
+ this.commentAuthorList = new CommentAuthorList(authors);
3164
+ }
3165
+ }
2611
3166
  };
2612
3167
  //#endregion
2613
3168
  //#region src/file/shape/paragraph/text.ts
@@ -10971,6 +11526,7 @@ var Compiler = class {
10971
11526
  path: "ppt/notesMasters/_rels/notesMaster1.xml.rels"
10972
11527
  };
10973
11528
  }
11529
+ if (file.CommentAuthorList) file.PresentationWrapper.Relationships.addRelationship(file.PresentationWrapper.Relationships.RelationshipCount + 1, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/commentAuthors", "commentAuthors.xml");
10974
11530
  const presentationXml = this.formatter.formatToXml(file.PresentationWrapper.View, context, declaration);
10975
11531
  let currentImageCount = 0;
10976
11532
  const mediaData = getReferencedMedia(presentationXml, file.Media.Array);
@@ -11050,6 +11606,7 @@ var Compiler = class {
11050
11606
  data: replacedSlideXml,
11051
11607
  path: `ppt/slides/slide${i + 1}.xml`
11052
11608
  };
11609
+ if (file.SlideCommentLists[i]) slideWrapper.Relationships.addRelationship(slideWrapper.Relationships.RelationshipCount + 1, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", `../comments/comment${i + 1}.xml`);
11053
11610
  mapping[`SlideRelationships${i}`] = {
11054
11611
  data: xml(this.formatter.format(slideWrapper.Relationships, context), { declaration: false }),
11055
11612
  path: `ppt/slides/_rels/slide${i + 1}.xml.rels`
@@ -11108,6 +11665,15 @@ var Compiler = class {
11108
11665
  standalone: "yes"
11109
11666
  } })), { level: 0 }];
11110
11667
  }
11668
+ if (file.CommentAuthorList) files["ppt/commentAuthors.xml"] = [textToUint8Array(xml(this.formatter.format(file.CommentAuthorList, context), {
11669
+ declaration,
11670
+ indent
11671
+ })), { level: 0 }];
11672
+ const commentLists = file.SlideCommentLists;
11673
+ for (let i = 0; i < commentLists.length; i++) if (commentLists[i]) files[`ppt/comments/comment${i + 1}.xml`] = [textToUint8Array(xml(this.formatter.format(commentLists[i], context), {
11674
+ declaration,
11675
+ indent
11676
+ })), { level: 0 }];
11111
11677
  for (const image of file.Media.Array) {
11112
11678
  files[`ppt/media/${image.fileName}`] = [image.data, { level: 0 }];
11113
11679
  if (image.type === "svg" && "fallback" in image) {