wordinweb 0.1.17 → 0.1.18

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.js CHANGED
@@ -2358,6 +2358,7 @@ var BORDER_STYLE_MAP = {
2358
2358
  dashed: "dashed",
2359
2359
  dotDash: "dotDash",
2360
2360
  dotDotDash: "dotDotDash",
2361
+ thinThickSmallGap: "thinThickSmallGap",
2361
2362
  triple: "triple",
2362
2363
  wave: "wave",
2363
2364
  none: "none",
@@ -4215,7 +4216,11 @@ function parseDrawing(drawing, ctx) {
4215
4216
  x2: emuToPx(x2),
4216
4217
  y2: emuToPx(y2),
4217
4218
  color,
4218
- weight: Math.max(weightEmu / EMU_PER_PT * (4 / 3), 0.75)
4219
+ weight: Math.max(weightEmu / EMU_PER_PT * (4 / 3), 0.75),
4220
+ style: (() => {
4221
+ const dash = attr(child(lnEl, "prstDash"), "val");
4222
+ return dash === "dot" || dash === "sysDot" ? "dotted" : dash && dash !== "solid" ? "dashed" : "single";
4223
+ })()
4219
4224
  });
4220
4225
  }
4221
4226
  const custGeom = child(spPr, "custGeom");
@@ -4772,6 +4777,7 @@ function resolveDiagramDrawing(relIds, ctx) {
4772
4777
  function collectDiagramShapes(dspRoot, ctx, out, dx, dy) {
4773
4778
  const spTree = findDescendant(dspRoot, "spTree");
4774
4779
  if (!spTree) return;
4780
+ let smartArtNodeIndex = 0;
4775
4781
  const emitShape = (sp, ox, oy, sx, sy) => {
4776
4782
  const spPr = child(sp, "spPr");
4777
4783
  if (!spPr) return;
@@ -4803,10 +4809,15 @@ function collectDiagramShapes(dspRoot, ctx, out, dx, dy) {
4803
4809
  x2: dx + emuToPx(x2),
4804
4810
  y2: dy + emuToPx(y2),
4805
4811
  color: stroke.color,
4806
- weight: Math.max(stroke.width, 0.75)
4812
+ weight: Math.max(stroke.width, 0.75),
4813
+ style: (() => {
4814
+ const dash = attr(child(lnEl, "prstDash"), "val");
4815
+ return dash === "dot" || dash === "sysDot" ? "dotted" : dash && dash !== "solid" ? "dashed" : "single";
4816
+ })()
4807
4817
  });
4808
4818
  }
4809
4819
  } else {
4820
+ const nodeIndex = smartArtNodeIndex++;
4810
4821
  const fill = child(spPr, "noFill") ? void 0 : solidFillColor(spPr, ctx.theme);
4811
4822
  if ((fill || stroke) && w > 0 && h > 0) {
4812
4823
  const d = presetPathD(prst, w, h, child(child(spPr, "prstGeom"), "avLst"));
@@ -4820,7 +4831,8 @@ function collectDiagramShapes(dspRoot, ctx, out, dx, dy) {
4820
4831
  viewW: w,
4821
4832
  viewH: h,
4822
4833
  fill,
4823
- stroke
4834
+ stroke,
4835
+ smartArtNodeIndex: nodeIndex
4824
4836
  });
4825
4837
  }
4826
4838
  }
@@ -5555,12 +5567,192 @@ function descendant2(node, name) {
5555
5567
  }
5556
5568
  return void 0;
5557
5569
  }
5570
+ function descendants3(node, name, out = []) {
5571
+ if (localName(node.name) === name) out.push(node);
5572
+ for (const child4 of node.children) descendants3(child4, name, out);
5573
+ return out;
5574
+ }
5575
+ function smartArtDrawingPart(doc, drawing) {
5576
+ const relIds = descendant2(drawing, "relIds");
5577
+ const dataRel = relIds ? doc.documentRels.get(attr(relIds, "dm") ?? "") : void 0;
5578
+ if (!dataRel || dataRel.external) return null;
5579
+ const dataXml = doc.pkg.text(dataRel.target);
5580
+ if (!dataXml) return null;
5581
+ const drawingRelId = attr(descendant2(parseXml(dataXml), "dataModelExt"), "relId");
5582
+ if (drawingRelId) {
5583
+ const hostRel = doc.documentRels.get(drawingRelId);
5584
+ if (hostRel && !hostRel.external) return hostRel.target;
5585
+ const relsXml = doc.pkg.text(relsPathFor(dataRel.target));
5586
+ if (relsXml) {
5587
+ const dataRelEntry = parseRelationships(parseXml(relsXml), dataRel.target).get(drawingRelId);
5588
+ if (dataRelEntry && !dataRelEntry.external) return dataRelEntry.target;
5589
+ }
5590
+ }
5591
+ for (const rel of doc.documentRels.values()) {
5592
+ if (!rel.external && rel.type.endsWith("/diagramDrawing")) return rel.target;
5593
+ }
5594
+ return null;
5595
+ }
5596
+ function smartArtNodeShapes(root) {
5597
+ return descendants3(root, "sp").filter((shape) => {
5598
+ const spPr = shape.children.find((child4) => localName(child4.name) === "spPr");
5599
+ if (!spPr) return false;
5600
+ const name = attr(descendant2(shape, "cNvPr"), "name") ?? "";
5601
+ const geometry = (attr(descendant2(spPr, "prstGeom"), "prst") ?? "").toLowerCase();
5602
+ return !/^connector\b/i.test(name) && geometry !== "line" && !geometry.includes("connector");
5603
+ });
5604
+ }
5605
+ function smartArtNodeShapeProperties(root) {
5606
+ return smartArtNodeShapes(root).flatMap((shape) => {
5607
+ const spPr = shape.children.find((child4) => localName(child4.name) === "spPr");
5608
+ return spPr ? [spPr] : [];
5609
+ });
5610
+ }
5611
+ function replaceTextLeaves(root, value) {
5612
+ const leaves = descendants3(root, "t").filter((node) => node.children.length === 0);
5613
+ if (!leaves.length) return false;
5614
+ leaves[0].text = value;
5615
+ for (const leaf of leaves.slice(1)) leaf.text = "";
5616
+ return true;
5617
+ }
5618
+ function directChild(node, name) {
5619
+ return node.children.find((child4) => localName(child4.name) === name);
5620
+ }
5621
+ function textColor(rPr) {
5622
+ const color = attr(descendant2(directChild(rPr ?? el("a:rPr"), "solidFill"), "srgbClr"), "val") ?? attr(descendant2(directChild(rPr ?? el("a:rPr"), "solidFill"), "sysClr"), "lastClr");
5623
+ return `#${(color ?? "000000").toUpperCase()}`;
5624
+ }
5625
+ function applyTextFormat(root, format) {
5626
+ for (const run of descendants3(root, "r")) {
5627
+ let rPr = directChild(run, "rPr");
5628
+ if (!rPr) {
5629
+ rPr = el("a:rPr");
5630
+ run.children.unshift(rPr);
5631
+ }
5632
+ rPr.attrs.sz = String(Math.round(format.fontSizePt * 100));
5633
+ rPr.attrs.b = format.bold ? "1" : "0";
5634
+ rPr.attrs.i = format.italic ? "1" : "0";
5635
+ for (const name of ["latin", "ea", "cs"]) {
5636
+ let font = directChild(rPr, name);
5637
+ if (!font) {
5638
+ font = el(`a:${name}`);
5639
+ rPr.children.push(font);
5640
+ }
5641
+ font.attrs.typeface = format.fontFamily;
5642
+ }
5643
+ rPr.children = rPr.children.filter((child4) => {
5644
+ const name = localName(child4.name);
5645
+ return !["solidFill", "noFill", "gradFill", "pattFill"].includes(name);
5646
+ });
5647
+ rPr.children.unshift(el("a:solidFill", {}, [el("a:srgbClr", { val: format.color.replace(/^#/, "").toUpperCase() })]));
5648
+ }
5649
+ for (const paragraph of descendants3(root, "p")) {
5650
+ let pPr = directChild(paragraph, "pPr");
5651
+ if (!pPr) {
5652
+ pPr = el("a:pPr");
5653
+ paragraph.children.unshift(pPr);
5654
+ }
5655
+ pPr.attrs.algn = format.alignment === "center" ? "ctr" : format.alignment === "right" ? "r" : "l";
5656
+ }
5657
+ }
5658
+ function smartArtFillColor(doc, drawing, nodeIndex = 0) {
5659
+ const part = smartArtDrawingPart(doc, drawing);
5660
+ const xml = part ? doc.pkg.text(part) : void 0;
5661
+ if (!xml) return null;
5662
+ const spPr = smartArtNodeShapeProperties(parseXml(xml))[nodeIndex];
5663
+ if (!spPr || spPr.children.some((child4) => localName(child4.name) === "noFill")) return null;
5664
+ const solidFill = spPr.children.find((child4) => localName(child4.name) === "solidFill");
5665
+ if (!solidFill) return null;
5666
+ const color = attr(descendant2(solidFill, "srgbClr"), "val") ?? attr(descendant2(solidFill, "sysClr"), "lastClr");
5667
+ return `#${(color ?? COLORS[0]).toUpperCase()}`;
5668
+ }
5669
+ function setSmartArtFill(doc, drawing, color, nodeIndex) {
5670
+ const part = smartArtDrawingPart(doc, drawing);
5671
+ const xml = part ? doc.pkg.text(part) : void 0;
5672
+ if (!part || !xml) return false;
5673
+ const root = parseXml(xml);
5674
+ const allNodes = smartArtNodeShapeProperties(root);
5675
+ const nodes = nodeIndex === void 0 ? allNodes : allNodes.slice(nodeIndex, nodeIndex + 1);
5676
+ if (!nodes.length) return false;
5677
+ for (const spPr of nodes) {
5678
+ spPr.children = spPr.children.filter((child4) => {
5679
+ const name = localName(child4.name);
5680
+ return !["solidFill", "noFill", "gradFill", "pattFill", "blipFill", "grpFill"].includes(name);
5681
+ });
5682
+ const fill = color ? el("a:solidFill", {}, [el("a:srgbClr", { val: color.replace(/^#/, "").toUpperCase() })]) : el("a:noFill");
5683
+ const lineIndex = spPr.children.findIndex((child4) => localName(child4.name) === "ln");
5684
+ spPr.children.splice(lineIndex === -1 ? spPr.children.length : lineIndex, 0, fill);
5685
+ }
5686
+ doc.pkg.raw()[part] = strToU8(serializeXml(root, true));
5687
+ doc.markPackageResourceChanged();
5688
+ doc.refresh();
5689
+ return true;
5690
+ }
5691
+ function setSmartArtNodeText(doc, drawing, nodeIndex, value) {
5692
+ const relIds = descendant2(drawing, "relIds");
5693
+ const dataRel = relIds ? doc.documentRels.get(attr(relIds, "dm") ?? "") : void 0;
5694
+ const drawingPart = smartArtDrawingPart(doc, drawing);
5695
+ const dataXml = dataRel && !dataRel.external ? doc.pkg.text(dataRel.target) : void 0;
5696
+ const drawingXml = drawingPart ? doc.pkg.text(drawingPart) : void 0;
5697
+ if (!dataRel || dataRel.external || !drawingPart || !dataXml || !drawingXml) return false;
5698
+ const drawingRoot = parseXml(drawingXml);
5699
+ const shape = smartArtNodeShapes(drawingRoot)[nodeIndex];
5700
+ const modelId = shape ? attr(shape, "modelId") : void 0;
5701
+ if (!shape || !modelId) return false;
5702
+ const dataRoot = parseXml(dataXml);
5703
+ const point = descendants3(dataRoot, "pt").find((candidate) => attr(candidate, "modelId") === modelId);
5704
+ if (!point || !replaceTextLeaves(shape, value) || !replaceTextLeaves(point, value)) return false;
5705
+ doc.pkg.raw()[drawingPart] = strToU8(serializeXml(drawingRoot, true));
5706
+ doc.pkg.raw()[dataRel.target] = strToU8(serializeXml(dataRoot, true));
5707
+ doc.markPackageResourceChanged();
5708
+ doc.refresh();
5709
+ return true;
5710
+ }
5711
+ function smartArtTextFormat(doc, drawing, nodeIndex = 0) {
5712
+ const part = smartArtDrawingPart(doc, drawing);
5713
+ const xml = part ? doc.pkg.text(part) : void 0;
5714
+ if (!xml) return null;
5715
+ const shape = smartArtNodeShapes(parseXml(xml))[nodeIndex];
5716
+ const rPr = shape ? descendant2(shape, "rPr") : void 0;
5717
+ if (!shape || !rPr) return null;
5718
+ const alignment = attr(descendant2(shape, "pPr"), "algn");
5719
+ return {
5720
+ fontFamily: attr(descendant2(rPr, "latin"), "typeface") ?? "Calibri",
5721
+ fontSizePt: Number(attr(rPr, "sz") ?? "1200") / 100,
5722
+ color: textColor(rPr),
5723
+ bold: attr(rPr, "b") === "1",
5724
+ italic: attr(rPr, "i") === "1",
5725
+ alignment: alignment === "r" ? "right" : alignment === "ctr" ? "center" : "left"
5726
+ };
5727
+ }
5728
+ function setSmartArtTextFormat(doc, drawing, format, nodeIndex) {
5729
+ const relIds = descendant2(drawing, "relIds");
5730
+ const dataRel = relIds ? doc.documentRels.get(attr(relIds, "dm") ?? "") : void 0;
5731
+ const drawingPart = smartArtDrawingPart(doc, drawing);
5732
+ const dataXml = dataRel && !dataRel.external ? doc.pkg.text(dataRel.target) : void 0;
5733
+ const drawingXml = drawingPart ? doc.pkg.text(drawingPart) : void 0;
5734
+ if (!dataRel || dataRel.external || !drawingPart || !dataXml || !drawingXml) return false;
5735
+ const drawingRoot = parseXml(drawingXml);
5736
+ const allShapes = smartArtNodeShapes(drawingRoot);
5737
+ const shapes = nodeIndex === void 0 ? allShapes : allShapes.slice(nodeIndex, nodeIndex + 1);
5738
+ if (!shapes.length) return false;
5739
+ const modelIds = new Set(shapes.map((shape) => attr(shape, "modelId")).filter((value) => !!value));
5740
+ const dataRoot = parseXml(dataXml);
5741
+ const points = descendants3(dataRoot, "pt").filter((point) => modelIds.has(attr(point, "modelId") ?? ""));
5742
+ for (const shape of shapes) applyTextFormat(shape, format);
5743
+ for (const point of points) applyTextFormat(point, format);
5744
+ doc.pkg.raw()[drawingPart] = strToU8(serializeXml(drawingRoot, true));
5745
+ doc.pkg.raw()[dataRel.target] = strToU8(serializeXml(dataRoot, true));
5746
+ doc.markPackageResourceChanged();
5747
+ doc.refresh();
5748
+ return true;
5749
+ }
5558
5750
  function escapeXml(value) {
5559
5751
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5560
5752
  }
5561
5753
  function normalizeSmartArtData(data) {
5562
5754
  const items = data.items.map((item) => item.trim()).filter(Boolean);
5563
- return { layout: data.layout, items: items.length ? items : ["First idea", "Second idea", "Third idea"] };
5755
+ return { layout: data.layout, items: items.length ? items : [""] };
5564
5756
  }
5565
5757
  function line(modelId, x1, y1, x2, y2) {
5566
5758
  return { modelId, x: x1, y: y1, width: x2 - x1, height: y2 - y1, geometry: "line" };
@@ -5651,13 +5843,13 @@ function buildSmartArtDataXml(input, drawingRelId) {
5651
5843
  (text, index) => `<dgm:pt modelId="${index + 1}"><dgm:prSet/><dgm:spPr/><dgm:t><a:bodyPr/><a:lstStyle/><a:p><a:r><a:rPr lang="en-US"/><a:t>${escapeXml(text)}</a:t></a:r></a:p></dgm:t></dgm:pt>`
5652
5844
  ).join("");
5653
5845
  const connections = data.items.map(
5654
- (_, index) => `<dgm:cxn modelId="${data.items.length + index + 1}" srcId="0" destId="${index + 1}" srcOrd="${index}" destOrd="0"/>`
5846
+ (_, index) => `<dgm:cxn modelId="${data.items.length + index + 1}" type="parOf" srcId="0" destId="${index + 1}" srcOrd="${index}" destOrd="0" presId=""/>`
5655
5847
  ).join("");
5656
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><dgm:dataModel xmlns:dgm="${NS_DGM}" xmlns:a="${NS_A}"><dgm:ptLst><dgm:pt modelId="0" type="doc"><dgm:prSet loTypeId="urn:wordinweb:smartart:${data.layout}" loCatId="${data.layout}"/><dgm:spPr/><dgm:t><a:bodyPr/><a:lstStyle/><a:p/></dgm:t></dgm:pt>${points}</dgm:ptLst><dgm:cxnLst>${connections}</dgm:cxnLst><dgm:bg/><dgm:whole/><dgm:extLst><a:ext uri="http://schemas.microsoft.com/office/drawing/2008/diagram"><dsp:dataModelExt xmlns:dsp="${NS_DSP}" relId="${escapeXml(drawingRelId)}" minVer="${NS_DGM}"/></a:ext></dgm:extLst></dgm:dataModel>`;
5848
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><dgm:dataModel xmlns:dgm="${NS_DGM}" xmlns:a="${NS_A}"><dgm:ptLst><dgm:pt modelId="0" type="doc"><dgm:prSet loTypeId="urn:wordinweb:smartart:${data.layout}" loCatId="${data.layout}" qsTypeId="urn:wordinweb:smartart:style" qsCatId="simple" csTypeId="urn:wordinweb:smartart:colors" csCatId="accent1"/><dgm:spPr/><dgm:t><a:bodyPr/><a:lstStyle/><a:p/></dgm:t></dgm:pt>${points}</dgm:ptLst><dgm:cxnLst>${connections}</dgm:cxnLst><dgm:bg/><dgm:whole/><dgm:extLst><a:ext uri="http://schemas.microsoft.com/office/drawing/2008/diagram"><dsp:dataModelExt xmlns:dsp="${NS_DSP}" relId="${escapeXml(drawingRelId)}" minVer="${NS_DGM}"/></a:ext></dgm:extLst></dgm:dataModel>`;
5657
5849
  }
5658
5850
  function buildSmartArtLayoutXml(input) {
5659
5851
  const data = normalizeSmartArtData(input);
5660
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><dgm:layoutDef xmlns:dgm="${NS_DGM}" xmlns:a="${NS_A}" uniqueId="urn:wordinweb:smartart:${data.layout}"><dgm:title val="${data.layout}"/><dgm:desc val="WordInWeb ${data.layout} diagram"/><dgm:catLst><dgm:cat type="${data.layout}" pri="1000"/></dgm:catLst><dgm:layoutNode name="root"><dgm:alg type="composite"/></dgm:layoutNode></dgm:layoutDef>`;
5852
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><dgm:layoutDef xmlns:dgm="${NS_DGM}" xmlns:a="${NS_A}" uniqueId="urn:wordinweb:smartart:${data.layout}"><dgm:title val="${data.layout}"/><dgm:desc val="WordInWeb ${data.layout} diagram"/><dgm:catLst><dgm:cat type="${data.layout}" pri="1000"/></dgm:catLst><dgm:layoutNode name="diagram"><dgm:alg type="lin"/><dgm:shape/><dgm:presOf/><dgm:constrLst><dgm:constr type="w" for="ch" forName="node" refType="w"/><dgm:constr op="equ" type="h" for="ch" forName="node"/><dgm:constr op="equ" type="primFontSz" for="ch" forName="node" val="65"/></dgm:constrLst><dgm:ruleLst/><dgm:forEach axis="ch" ptType="node"><dgm:layoutNode name="node" styleLbl="node0"><dgm:varLst><dgm:bulletEnabled val="true"/></dgm:varLst><dgm:alg type="tx"/><dgm:shape type="roundRect"/><dgm:presOf axis="desOrSelf" ptType="node"/><dgm:constrLst><dgm:constr type="tMarg" refType="primFontSz" fact="0.3"/><dgm:constr type="bMarg" refType="primFontSz" fact="0.3"/><dgm:constr type="lMarg" refType="primFontSz" fact="0.3"/><dgm:constr type="rMarg" refType="primFontSz" fact="0.3"/></dgm:constrLst><dgm:ruleLst><dgm:rule type="primFontSz" val="5"/></dgm:ruleLst></dgm:layoutNode></dgm:forEach></dgm:layoutNode></dgm:layoutDef>`;
5661
5853
  }
5662
5854
  function buildSmartArtStyleXml() {
5663
5855
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><dgm:styleDef xmlns:dgm="${NS_DGM}" xmlns:a="${NS_A}" uniqueId="urn:wordinweb:smartart:style"><dgm:title val="WordInWeb"/><dgm:desc val="WordInWeb diagram style"/><dgm:catLst><dgm:cat type="simple" pri="1000"/></dgm:catLst><dgm:scene3d><a:camera prst="orthographicFront"/><a:lightRig rig="threePt" dir="t"/></dgm:scene3d><dgm:styleLbl name="node0"><dgm:scene3d><a:camera prst="orthographicFront"/><a:lightRig rig="threePt" dir="t"/></dgm:scene3d><dgm:sp3d/><dgm:txPr/><dgm:style><a:lnRef idx="2"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="1"><a:schemeClr val="accent1"/></a:fillRef><a:effectRef idx="0"><a:schemeClr val="accent1"/></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="lt1"/></a:fontRef></dgm:style></dgm:styleLbl></dgm:styleDef>`;
@@ -5712,12 +5904,12 @@ function setSmartArtData(doc, drawing, input) {
5712
5904
  const dataRootXml = doc.pkg.text(dataRel.target);
5713
5905
  if (!dataRootXml) return false;
5714
5906
  const drawingRelId = attr(descendant2(parseXml(dataRootXml), "dataModelExt"), "relId");
5715
- const drawingRel = drawingRelId ? doc.documentRels.get(drawingRelId) : void 0;
5716
- if (!drawingRel || drawingRel.external) return false;
5907
+ const drawingPart = smartArtDrawingPart(doc, drawing);
5908
+ if (!drawingRelId || !drawingPart) return false;
5717
5909
  const data = normalizeSmartArtData(input);
5718
- doc.pkg.raw()[dataRel.target] = strToU8(buildSmartArtDataXml(data, drawingRel.id));
5910
+ doc.pkg.raw()[dataRel.target] = strToU8(buildSmartArtDataXml(data, drawingRelId));
5719
5911
  doc.pkg.raw()[layoutRel.target] = strToU8(buildSmartArtLayoutXml(data));
5720
- doc.pkg.raw()[drawingRel.target] = strToU8(buildSmartArtDrawingXml(data));
5912
+ doc.pkg.raw()[drawingPart] = strToU8(buildSmartArtDrawingXml(data));
5721
5913
  doc.markPackageResourceChanged();
5722
5914
  doc.refresh();
5723
5915
  return true;
@@ -14558,7 +14750,7 @@ var Engine = class {
14558
14750
  y1: by + l.y1,
14559
14751
  x2: bx + l.x2,
14560
14752
  y2: by + l.y2,
14561
- border: { style: "single", width: l.weight, color: l.color, space: 0 }
14753
+ border: { style: l.style ?? "single", width: l.weight, color: l.color, space: 0 }
14562
14754
  });
14563
14755
  }
14564
14756
  for (const pth of span.drawing.paths ?? []) {
@@ -14585,7 +14777,16 @@ var Engine = class {
14585
14777
  src: span.drawing.srcDrawing,
14586
14778
  anchored: false,
14587
14779
  belowText: true,
14588
- smartArt: !!span.drawing.smartArt
14780
+ smartArt: !!span.drawing.smartArt,
14781
+ smartArtNodes: span.drawing.smartArt ? (span.drawing.paths ?? []).flatMap((path2) => path2.smartArtNodeIndex === void 0 ? [] : [{
14782
+ index: path2.smartArtNodeIndex,
14783
+ x: path2.x,
14784
+ y: path2.y,
14785
+ width: path2.width,
14786
+ height: path2.height
14787
+ }]) : void 0,
14788
+ chartData: span.drawing.chart,
14789
+ smartArtData: span.drawing.smartArt
14589
14790
  });
14590
14791
  }
14591
14792
  for (const ts of span.drawing.texts ?? []) {
@@ -15061,7 +15262,7 @@ var Engine = class {
15061
15262
  const y1 = oy + l.y1 - fy;
15062
15263
  const x2 = ox + l.x2 - fx;
15063
15264
  const y2 = oy + l.y2 - fy;
15064
- page.items.push({ kind: "edge", x1, y1, x2, y2, border: { style: "single", width: l.weight, color: l.color, space: 0 }, ...rotate ? { rotate: rotate(Math.min(x1, x2), Math.min(y1, y2)) } : {}, ...shape.behind ? { behind: true } : { front: true }, z: shape.z });
15265
+ page.items.push({ kind: "edge", x1, y1, x2, y2, border: { style: l.style ?? "single", width: l.weight, color: l.color, space: 0 }, ...rotate ? { rotate: rotate(Math.min(x1, x2), Math.min(y1, y2)) } : {}, ...shape.behind ? { behind: true } : { front: true }, z: shape.z });
15065
15266
  }
15066
15267
  for (const img of shape.images) {
15067
15268
  const x = ox + img.x - fx;
@@ -18101,6 +18302,10 @@ function printPages(root, pageWidthPx, pageHeightPx) {
18101
18302
  @page { size: ${pageWidthPx / 96}in ${pageHeightPx / 96}in; margin: 0; }
18102
18303
  html, body { margin: 0; padding: 0; }
18103
18304
  .dxw-pages { display: block !important; padding: 0 !important; gap: 0 !important; }
18305
+ .dxw-pages, .dxw-pages * {
18306
+ -webkit-print-color-adjust: exact !important;
18307
+ print-color-adjust: exact !important;
18308
+ }
18104
18309
  .dxw-page { box-shadow: none !important; margin: 0 !important; break-after: page; }
18105
18310
  .dxw-comment-card, .dxw-hf-marker { display: none !important; }
18106
18311
  `;
@@ -18762,9 +18967,17 @@ function renderItem(doc, item, urls, interactive) {
18762
18967
  hit.dataset.dxwDrawing = "1";
18763
18968
  if (item.ink) hit.dataset.dxwInk = "1";
18764
18969
  if (item.smartArt) hit.dataset.dxwSmartArt = "1";
18765
- if (item.height < 12) {
18970
+ for (const node of item.smartArtNodes ?? []) {
18971
+ const nodeHit = document.createElement("div");
18972
+ nodeHit.dataset.dxwSmartArtNode = String(node.index);
18973
+ nodeHit.style.cssText = `position:absolute;left:${node.x}px;top:${node.y}px;width:${node.width}px;height:${node.height}px;cursor:move;`;
18974
+ hit.appendChild(nodeHit);
18975
+ }
18976
+ if (item.width < 12 || item.height < 12) {
18766
18977
  const target = document.createElement("div");
18767
- target.style.cssText = `position:absolute;left:0;top:${-(12 - item.height) / 2}px;width:100%;height:12px;cursor:move;`;
18978
+ const targetWidth = Math.max(item.width, 12);
18979
+ const targetHeight = Math.max(item.height, 12);
18980
+ target.style.cssText = `position:absolute;left:${-(targetWidth - item.width) / 2}px;top:${-(targetHeight - item.height) / 2}px;width:${targetWidth}px;height:${targetHeight}px;cursor:move;`;
18768
18981
  hit.appendChild(target);
18769
18982
  }
18770
18983
  return hit;
@@ -19481,6 +19694,8 @@ function renderEdge(x1, y1, x2, y2, border, rotate) {
19481
19694
  stroke.style.transformOrigin = "0 50%";
19482
19695
  if (border.style === "double") {
19483
19696
  stroke.style.background = `linear-gradient(180deg, ${border.color} 0 ${w}px, transparent ${w}px ${w * 2}px, ${border.color} ${w * 2}px ${w * 3}px)`;
19697
+ } else if (border.style === "thinThickSmallGap") {
19698
+ stroke.style.background = `linear-gradient(180deg, ${border.color} 0 25%, transparent 25% 50%, ${border.color} 50% 100%)`;
19484
19699
  } else if (dashPattern) {
19485
19700
  const on = Math.max(dashPattern[0] * border.width, 2);
19486
19701
  const period = on + Math.max(dashPattern[1] * border.width, 1);
@@ -19505,6 +19720,9 @@ function renderEdge(x1, y1, x2, y2, border, rotate) {
19505
19720
  if (border.style === "double") {
19506
19721
  el16.style.height = `${w * 3}px`;
19507
19722
  el16.style.background = `linear-gradient(180deg, ${border.color} 0 ${w}px, transparent ${w}px ${w * 2}px, ${border.color} ${w * 2}px ${w * 3}px)`;
19723
+ } else if (border.style === "thinThickSmallGap") {
19724
+ el16.style.height = `${w}px`;
19725
+ el16.style.background = `linear-gradient(180deg, ${border.color} 0 25%, transparent 25% 50%, ${border.color} 50% 100%)`;
19508
19726
  } else if (dashPattern) {
19509
19727
  const on = Math.max(dashPattern[0] * border.width, 2);
19510
19728
  const period = on + Math.max(dashPattern[1] * border.width, 1);
@@ -19533,6 +19751,9 @@ function renderEdge(x1, y1, x2, y2, border, rotate) {
19533
19751
  if (border.style === "double") {
19534
19752
  el16.style.width = `${w * 3}px`;
19535
19753
  el16.style.background = `linear-gradient(90deg, ${border.color} 0 ${w}px, transparent ${w}px ${w * 2}px, ${border.color} ${w * 2}px ${w * 3}px)`;
19754
+ } else if (border.style === "thinThickSmallGap") {
19755
+ el16.style.width = `${w}px`;
19756
+ el16.style.background = `linear-gradient(90deg, ${border.color} 0 25%, transparent 25% 50%, ${border.color} 50% 100%)`;
19536
19757
  } else if (dashPattern) {
19537
19758
  const on = Math.max(dashPattern[0] * border.width, 2);
19538
19759
  const period = on + Math.max(dashPattern[1] * border.width, 1);
@@ -20318,6 +20539,13 @@ function cellContextOf(doc, target) {
20318
20539
  }
20319
20540
  return null;
20320
20541
  }
20542
+ function cellShadingAt(doc, target) {
20543
+ const ctx = cellContextOf(doc, target);
20544
+ if (!ctx) return void 0;
20545
+ const shd = child(child(ctx.tc, "tcPr"), "shd");
20546
+ const fill = shd ? attr(shd, "fill") : void 0;
20547
+ return fill && fill.toLowerCase() !== "auto" ? `#${fill.replace(/^#/, "").toUpperCase()}` : null;
20548
+ }
20321
20549
  function emptyCellLike(tc, w) {
20322
20550
  const tcPr = tc.children.find((c) => localName(c.name) === "tcPr");
20323
20551
  return {
@@ -20811,12 +21039,13 @@ function resizeDrawing(doc, drawingEl, widthPx, heightPx) {
20811
21039
  for (const childNode of node.children) findLineExtent(childNode);
20812
21040
  };
20813
21041
  findLineExtent(drawingEl);
21042
+ const keepVertical = lineExtent ? parseInt(lineExtent.attrs[Object.keys(lineExtent.attrs).find((key) => localName(key) === "cx") ?? "cx"] ?? "0", 10) === 0 : false;
20814
21043
  const keepHorizontal = lineExtent ? parseInt(lineExtent.attrs[Object.keys(lineExtent.attrs).find((key) => localName(key) === "cy") ?? "cy"] ?? "0", 10) === 0 : false;
20815
21044
  let touched = false;
20816
21045
  const setExt = (el16) => {
20817
21046
  const cxKey = Object.keys(el16.attrs).find((k) => localName(k) === "cx") ?? "cx";
20818
21047
  const cyKey = Object.keys(el16.attrs).find((k) => localName(k) === "cy") ?? "cy";
20819
- el16.attrs[cxKey] = cx;
21048
+ el16.attrs[cxKey] = el16 === lineExtent && keepVertical ? "0" : cx;
20820
21049
  el16.attrs[cyKey] = el16 === lineExtent && keepHorizontal ? "0" : cy;
20821
21050
  touched = true;
20822
21051
  };
@@ -21164,6 +21393,10 @@ function insertBlankPageAt(doc, t, offset) {
21164
21393
  if (ordinaryPPr) {
21165
21394
  ordinaryPPr.children = ordinaryPPr.children.filter((c) => localName(c.name) !== "sectPr");
21166
21395
  }
21396
+ const blankPPr = ordinaryPPr ? cloneDeep(ordinaryPPr) : null;
21397
+ if (blankPPr) {
21398
+ blankPPr.children = blankPPr.children.filter((c) => localName(c.name) !== "numPr");
21399
+ }
21167
21400
  const headBefore = pEl.children.slice(0, runIndex).filter((c) => localName(c.name) !== "pPr");
21168
21401
  const tailAfter = pEl.children.slice(runIndex + 1).filter((c) => localName(c.name) !== "pPr");
21169
21402
  pEl.children = [
@@ -21177,7 +21410,7 @@ function insertBlankPageAt(doc, t, offset) {
21177
21410
  el4(`${rw}t`, { "xml:space": "preserve" }, [], "")
21178
21411
  ]);
21179
21412
  const blank = el4(pEl.name, { ...pEl.attrs }, [
21180
- ...ordinaryPPr ? [cloneDeep(ordinaryPPr)] : [],
21413
+ ...blankPPr ? [blankPPr] : [],
21181
21414
  anchorRun,
21182
21415
  breakRun()
21183
21416
  ]);
@@ -21812,19 +22045,21 @@ function insertShapeAt(doc, caretT, preset, text = "") {
21812
22045
  if (!caretRun || !parent || localName(caretRun.name) !== "r") return null;
21813
22046
  const w = prefixOf8(caretRun);
21814
22047
  const id = String(doc.nextDrawingId());
21815
- const isLine = preset === "line";
21816
- const width = isLine ? 240 : preset === "textBox" ? 240 : 192;
21817
- const height = isLine ? 2 : preset === "textBox" ? 72 : 96;
22048
+ const isLine = preset === "line" || preset === "verticalLine";
22049
+ const isVerticalLine = preset === "verticalLine";
22050
+ const width = isVerticalLine ? 2 : isLine ? 240 : preset === "textBox" ? 240 : 192;
22051
+ const height = isVerticalLine ? 240 : isLine ? 2 : preset === "textBox" ? 72 : 96;
21818
22052
  const cx = String(Math.round(width * EMU_PER_PX4));
21819
22053
  const cy = String(Math.round(height * EMU_PER_PX4));
21820
- const shapeCy = isLine ? "0" : cy;
21821
- const geometry = preset === "roundedRectangle" ? "roundRect" : preset === "rectangle" || preset === "textBox" ? "rect" : preset;
22054
+ const shapeCx = isVerticalLine ? "0" : cx;
22055
+ const shapeCy = isLine && !isVerticalLine ? "0" : cy;
22056
+ const geometry = isLine ? "line" : preset === "roundedRectangle" ? "roundRect" : preset === "rectangle" || preset === "textBox" ? "rect" : preset;
21822
22057
  const isTextBox = preset === "textBox";
21823
22058
  const shapeName = isLine ? `Line ${id}` : isTextBox ? `Text Box ${id}` : `Shape ${id}`;
21824
22059
  const spPr = [
21825
22060
  el6("a:xfrm", {}, [
21826
22061
  el6("a:off", { x: "0", y: "0" }),
21827
- el6("a:ext", { cx, cy: shapeCy })
22062
+ el6("a:ext", { cx: shapeCx, cy: shapeCy })
21828
22063
  ]),
21829
22064
  el6("a:prstGeom", { prst: geometry }, [el6("a:avLst")]),
21830
22065
  isTextBox || isLine ? el6("a:noFill") : el6("a:solidFill", {}, [el6("a:srgbClr", { val: "4472C4" })]),
@@ -21945,10 +22180,12 @@ function drawingLineStyle(drawing) {
21945
22180
  const spPr = descendant3(drawing, "spPr");
21946
22181
  if (!spPr) return null;
21947
22182
  const line2 = descendant3(drawing, "ln");
21948
- if (!line2) return { color: "#000000", width: 0.75 };
22183
+ if (!line2) return { color: "#000000", width: 0.75, dash: "solid" };
21949
22184
  const color = descendant3(line2, "srgbClr")?.attrs.val ?? "000000";
21950
22185
  const width = Math.max((parseInt(line2.attrs.w ?? "0", 10) || 0) / EMU_PER_PX4, 0.75);
21951
- return { color: `#${color.toUpperCase()}`, width };
22186
+ const dashValue = line2.children.find((child4) => localName(child4.name) === "prstDash")?.attrs.val;
22187
+ const dash = dashValue === "dot" || dashValue === "sysDot" ? "dotted" : dashValue && dashValue !== "solid" ? "dashed" : "solid";
22188
+ return { color: `#${color.toUpperCase()}`, width, dash };
21952
22189
  }
21953
22190
  function drawingFillColor(drawing) {
21954
22191
  const spPr = descendant3(drawing, "spPr");
@@ -21970,7 +22207,7 @@ function setDrawingFill(doc, drawing, color) {
21970
22207
  doc.refresh();
21971
22208
  return true;
21972
22209
  }
21973
- function setDrawingLineStyle(doc, drawing, color, widthPx) {
22210
+ function setDrawingLineStyle(doc, drawing, color, widthPx, dash = "solid") {
21974
22211
  const spPr = descendant3(drawing, "spPr");
21975
22212
  if (!spPr) return false;
21976
22213
  let line2 = spPr.children.find((child4) => localName(child4.name) === "ln");
@@ -21981,9 +22218,10 @@ function setDrawingLineStyle(doc, drawing, color, widthPx) {
21981
22218
  line2.attrs.w = String(Math.max(1, Math.round(widthPx * EMU_PER_PX4)));
21982
22219
  line2.children = line2.children.filter((child4) => {
21983
22220
  const name = localName(child4.name);
21984
- return name !== "solidFill" && name !== "noFill";
22221
+ return name !== "solidFill" && name !== "noFill" && name !== "prstDash";
21985
22222
  });
21986
22223
  line2.children.unshift(el6("a:solidFill", {}, [el6("a:srgbClr", { val: color.replace(/^#/, "").toUpperCase() })]));
22224
+ line2.children.splice(1, 0, el6("a:prstDash", { val: dash === "dotted" ? "dot" : dash === "dashed" ? "dash" : "solid" }));
21987
22225
  doc.refresh();
21988
22226
  return true;
21989
22227
  }
@@ -22444,6 +22682,12 @@ function requestTextInputDialog(anchor, options) {
22444
22682
  label.textContent = options.label ?? options.title;
22445
22683
  label.style.cssText = "display:grid;gap:5px;color:var(--dxw-toolbar-muted,#5f6368);font-size:12px;";
22446
22684
  const field = options.multiline ? doc.createElement("textarea") : doc.createElement("input");
22685
+ if (field instanceof HTMLInputElement) {
22686
+ field.type = options.inputType ?? "text";
22687
+ if (options.min !== void 0) field.min = String(options.min);
22688
+ if (options.max !== void 0) field.max = String(options.max);
22689
+ if (options.step !== void 0) field.step = String(options.step);
22690
+ }
22447
22691
  field.className = "dxw-input-dialog-field";
22448
22692
  field.value = options.value ?? "";
22449
22693
  field.placeholder = options.placeholder ?? "";
@@ -22499,59 +22743,463 @@ function requestTextInputDialog(anchor, options) {
22499
22743
  });
22500
22744
  });
22501
22745
  }
22502
-
22503
- // ../core/src/edit/objects.ts
22504
- var EMU_PER_PX6 = 9525;
22505
- var NS_WP5 = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing";
22506
- var NS_A4 = "http://schemas.openxmlformats.org/drawingml/2006/main";
22507
- var NS_PIC2 = "http://schemas.openxmlformats.org/drawingml/2006/picture";
22508
- var NS_R3 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
22509
- var NS_MC = "http://schemas.openxmlformats.org/markup-compatibility/2006";
22510
- var NS_AM3D = "http://schemas.microsoft.com/office/drawing/2017/model3d";
22511
- var NS_WP15 = "http://schemas.microsoft.com/office/word/2012/wordprocessingDrawing";
22512
- function el8(name, attrs = {}, children2 = [], text = "") {
22513
- return { name, attrs, children: children2, text };
22514
- }
22515
- function prefixOf9(node) {
22516
- return node.name.includes(":") ? node.name.slice(0, node.name.indexOf(":") + 1) : "";
22746
+ function requestNumberPairDialog(anchor, options) {
22747
+ const doc = anchor.ownerDocument;
22748
+ const win = doc.defaultView;
22749
+ return new Promise((resolve) => {
22750
+ const id = `dxw-number-pair-dialog-${++dialogSequence}`;
22751
+ const previousFocus = doc.activeElement instanceof HTMLElement ? doc.activeElement : null;
22752
+ const backdrop = doc.createElement("div");
22753
+ backdrop.className = "dxw-input-dialog-backdrop";
22754
+ backdrop.dataset.dxwNumberPairDialog = "";
22755
+ backdrop.style.cssText = "position:fixed;inset:0;z-index:var(--dxw-dialog-z-index,1000);display:grid;place-items:center;padding:16px;box-sizing:border-box;background:var(--dxw-dialog-backdrop,rgba(32,33,36,.38));";
22756
+ if (win) {
22757
+ const computed = win.getComputedStyle(anchor);
22758
+ for (const property of [
22759
+ "--dxw-toolbar-fg",
22760
+ "--dxw-toolbar-border",
22761
+ "--dxw-toolbar-muted",
22762
+ "--dxw-accent",
22763
+ "--dxw-accent-fg",
22764
+ "--dxw-popover-bg",
22765
+ "--dxw-popover-shadow",
22766
+ "--dxw-dialog-backdrop",
22767
+ "--dxw-dialog-z-index",
22768
+ "--dxw-dialog-width",
22769
+ "--dxw-dialog-radius",
22770
+ "--dxw-dialog-field-bg"
22771
+ ]) {
22772
+ const token = computed.getPropertyValue(property);
22773
+ if (token) backdrop.style.setProperty(property, token);
22774
+ }
22775
+ }
22776
+ const form = doc.createElement("form");
22777
+ form.className = "dxw-input-dialog dxw-number-pair-dialog";
22778
+ form.setAttribute("role", "dialog");
22779
+ form.setAttribute("aria-modal", "true");
22780
+ form.setAttribute("aria-labelledby", id);
22781
+ form.style.cssText = "width:min(var(--dxw-dialog-width,420px),calc(100vw - 32px));box-sizing:border-box;display:grid;gap:12px;padding:16px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:var(--dxw-dialog-radius,10px);color:var(--dxw-toolbar-fg,#3c4043);background:var(--dxw-popover-bg,#fff);box-shadow:var(--dxw-popover-shadow,0 8px 28px rgba(0,0,0,.24));font:13px system-ui,sans-serif;";
22782
+ const title = doc.createElement("strong");
22783
+ title.id = id;
22784
+ title.textContent = options.title;
22785
+ title.style.cssText = "font-size:15px;line-height:1.35;";
22786
+ form.appendChild(title);
22787
+ const fields = doc.createElement("div");
22788
+ fields.style.cssText = "display:grid;grid-template-columns:1fr 1fr;gap:10px;";
22789
+ const makeField = (labelText, value) => {
22790
+ const label = doc.createElement("label");
22791
+ label.textContent = labelText;
22792
+ label.style.cssText = "display:grid;gap:5px;color:var(--dxw-toolbar-muted,#5f6368);font-size:12px;";
22793
+ const input = doc.createElement("input");
22794
+ input.type = "number";
22795
+ input.required = true;
22796
+ input.value = String(value);
22797
+ if (options.min !== void 0) input.min = String(options.min);
22798
+ if (options.step !== void 0) input.step = String(options.step);
22799
+ input.setAttribute("aria-label", labelText);
22800
+ input.style.cssText = "width:100%;min-height:34px;box-sizing:border-box;padding:7px 9px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:6px;outline:none;color:var(--dxw-toolbar-fg,#3c4043);background:var(--dxw-dialog-field-bg,var(--dxw-popover-bg,#fff));font:13px system-ui,sans-serif;";
22801
+ label.appendChild(input);
22802
+ fields.appendChild(label);
22803
+ return input;
22804
+ };
22805
+ const first = makeField(options.firstLabel, options.value.first);
22806
+ const second = makeField(options.secondLabel, options.value.second);
22807
+ form.appendChild(fields);
22808
+ const actions = doc.createElement("div");
22809
+ actions.style.cssText = "display:flex;justify-content:flex-end;gap:7px;";
22810
+ const cancel = doc.createElement("button");
22811
+ cancel.type = "button";
22812
+ cancel.textContent = "Cancel";
22813
+ cancel.style.cssText = "min-height:32px;padding:0 12px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:7px;color:var(--dxw-toolbar-fg,#3c4043);background:var(--dxw-popover-bg,#fff);cursor:pointer;font:600 12px system-ui,sans-serif;";
22814
+ const submit = doc.createElement("button");
22815
+ submit.type = "submit";
22816
+ submit.textContent = "Apply";
22817
+ submit.style.cssText = "min-height:32px;padding:0 14px;border:1px solid transparent;border-radius:7px;color:var(--dxw-accent-fg,#fff);background:var(--dxw-accent,#1a73e8);cursor:pointer;font:600 12px system-ui,sans-serif;";
22818
+ actions.append(cancel, submit);
22819
+ form.appendChild(actions);
22820
+ backdrop.appendChild(form);
22821
+ doc.body.appendChild(backdrop);
22822
+ let finished = false;
22823
+ const finish = (value) => {
22824
+ if (finished) return;
22825
+ finished = true;
22826
+ doc.removeEventListener("keydown", onKeyDown, true);
22827
+ backdrop.remove();
22828
+ previousFocus?.focus({ preventScroll: true });
22829
+ resolve(value);
22830
+ };
22831
+ const onKeyDown = (event) => {
22832
+ if (event.key === "Escape") {
22833
+ event.preventDefault();
22834
+ finish(null);
22835
+ }
22836
+ };
22837
+ doc.addEventListener("keydown", onKeyDown, true);
22838
+ backdrop.addEventListener("mousedown", (event) => {
22839
+ if (event.target === backdrop) finish(null);
22840
+ });
22841
+ cancel.addEventListener("click", () => finish(null));
22842
+ form.addEventListener("submit", (event) => {
22843
+ event.preventDefault();
22844
+ const firstValue = Number(first.value);
22845
+ const secondValue = Number(second.value);
22846
+ if (!Number.isFinite(firstValue) || !Number.isFinite(secondValue)) {
22847
+ form.reportValidity();
22848
+ return;
22849
+ }
22850
+ finish({ first: firstValue, second: secondValue });
22851
+ });
22852
+ requestAnimationFrame(() => {
22853
+ first.focus({ preventScroll: true });
22854
+ first.select();
22855
+ });
22856
+ });
22517
22857
  }
22518
- function insertAfterCaretRun(doc, caretT, content) {
22519
- const caretRun = doc.findParentOf(caretT);
22520
- const parent = caretRun && doc.findParentOf(caretRun);
22521
- if (!caretRun || !parent || localName(caretRun.name) !== "r") return false;
22522
- const w = prefixOf9(caretRun);
22523
- parent.children.splice(parent.children.indexOf(caretRun) + 1, 0, el8(`${w}r`, {}, [content]));
22524
- doc.refresh();
22525
- return true;
22858
+ function requestColorDialog(anchor, options) {
22859
+ const doc = anchor.ownerDocument;
22860
+ const win = doc.defaultView;
22861
+ return new Promise((resolve) => {
22862
+ const id = `dxw-color-dialog-${++dialogSequence}`;
22863
+ const previousFocus = doc.activeElement instanceof HTMLElement ? doc.activeElement : null;
22864
+ const backdrop = doc.createElement("div");
22865
+ backdrop.className = "dxw-input-dialog-backdrop";
22866
+ backdrop.dataset.dxwColorDialog = "";
22867
+ backdrop.style.cssText = "position:fixed;inset:0;z-index:var(--dxw-dialog-z-index,1000);display:grid;place-items:center;padding:16px;box-sizing:border-box;background:var(--dxw-dialog-backdrop,rgba(32,33,36,.38));";
22868
+ if (win) {
22869
+ const computed = win.getComputedStyle(anchor);
22870
+ for (const property of [
22871
+ "--dxw-toolbar-fg",
22872
+ "--dxw-toolbar-border",
22873
+ "--dxw-toolbar-muted",
22874
+ "--dxw-accent",
22875
+ "--dxw-accent-fg",
22876
+ "--dxw-popover-bg",
22877
+ "--dxw-popover-shadow",
22878
+ "--dxw-dialog-backdrop",
22879
+ "--dxw-dialog-z-index",
22880
+ "--dxw-dialog-width",
22881
+ "--dxw-dialog-radius",
22882
+ "--dxw-dialog-field-bg"
22883
+ ]) {
22884
+ const token = computed.getPropertyValue(property);
22885
+ if (token) backdrop.style.setProperty(property, token);
22886
+ }
22887
+ }
22888
+ const form = doc.createElement("form");
22889
+ form.className = "dxw-input-dialog dxw-color-dialog";
22890
+ form.setAttribute("role", "dialog");
22891
+ form.setAttribute("aria-modal", "true");
22892
+ form.setAttribute("aria-labelledby", id);
22893
+ form.style.cssText = "width:min(var(--dxw-dialog-width,420px),calc(100vw - 32px));box-sizing:border-box;display:grid;gap:12px;padding:16px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:var(--dxw-dialog-radius,10px);color:var(--dxw-toolbar-fg,#3c4043);background:var(--dxw-popover-bg,#fff);box-shadow:var(--dxw-popover-shadow,0 8px 28px rgba(0,0,0,.24));font:13px system-ui,sans-serif;";
22894
+ const title = doc.createElement("strong");
22895
+ title.id = id;
22896
+ title.textContent = options.title;
22897
+ title.style.cssText = "font-size:15px;line-height:1.35;";
22898
+ form.appendChild(title);
22899
+ const initial = /^#?([0-9a-f]{6})$/i.exec(options.value ?? "")?.[1] ?? "4472C4";
22900
+ const colorLabel = doc.createElement("label");
22901
+ colorLabel.textContent = "Color";
22902
+ colorLabel.style.cssText = "display:grid;gap:5px;color:var(--dxw-toolbar-muted,#5f6368);font-size:12px;";
22903
+ const colorRow = doc.createElement("span");
22904
+ colorRow.style.cssText = "display:grid;grid-template-columns:42px 1fr;gap:7px;";
22905
+ const picker = doc.createElement("input");
22906
+ picker.type = "color";
22907
+ picker.value = `#${initial}`;
22908
+ picker.setAttribute("aria-label", `${options.title} color picker`);
22909
+ picker.style.cssText = "width:42px;height:34px;box-sizing:border-box;padding:2px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:6px;background:var(--dxw-dialog-field-bg,var(--dxw-popover-bg,#fff));";
22910
+ const field = doc.createElement("input");
22911
+ field.type = "text";
22912
+ field.value = `#${initial.toUpperCase()}`;
22913
+ field.required = true;
22914
+ field.pattern = "#?[0-9a-fA-F]{6}";
22915
+ field.setAttribute("aria-label", `${options.title} color`);
22916
+ field.style.cssText = "width:100%;min-height:34px;box-sizing:border-box;padding:7px 9px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:6px;outline:none;color:var(--dxw-toolbar-fg,#3c4043);background:var(--dxw-dialog-field-bg,var(--dxw-popover-bg,#fff));font:13px system-ui,sans-serif;";
22917
+ let noFill = null;
22918
+ picker.addEventListener("input", () => {
22919
+ field.value = picker.value.toUpperCase();
22920
+ if (noFill) noFill.checked = false;
22921
+ });
22922
+ field.addEventListener("input", () => {
22923
+ if (noFill) noFill.checked = false;
22924
+ const match = /^#?([0-9a-f]{6})$/i.exec(field.value.trim());
22925
+ if (match) picker.value = `#${match[1]}`;
22926
+ });
22927
+ colorRow.append(picker, field);
22928
+ colorLabel.appendChild(colorRow);
22929
+ form.appendChild(colorLabel);
22930
+ if (options.allowNone) {
22931
+ const noneLabel = doc.createElement("label");
22932
+ noneLabel.style.cssText = "display:flex;align-items:center;gap:7px;color:var(--dxw-toolbar-fg,#3c4043);font-size:12px;";
22933
+ noFill = doc.createElement("input");
22934
+ noFill.type = "checkbox";
22935
+ noFill.checked = options.value === null;
22936
+ noFill.setAttribute("aria-label", "No fill");
22937
+ noneLabel.append(noFill, doc.createTextNode("No fill"));
22938
+ form.appendChild(noneLabel);
22939
+ }
22940
+ const actions = doc.createElement("div");
22941
+ actions.style.cssText = "display:flex;justify-content:flex-end;gap:7px;";
22942
+ const cancel = doc.createElement("button");
22943
+ cancel.type = "button";
22944
+ cancel.textContent = "Cancel";
22945
+ cancel.style.cssText = "min-height:32px;padding:0 12px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:7px;color:var(--dxw-toolbar-fg,#3c4043);background:var(--dxw-popover-bg,#fff);cursor:pointer;font:600 12px system-ui,sans-serif;";
22946
+ const submit = doc.createElement("button");
22947
+ submit.type = "submit";
22948
+ submit.textContent = "Apply";
22949
+ submit.style.cssText = "min-height:32px;padding:0 14px;border:1px solid transparent;border-radius:7px;color:var(--dxw-accent-fg,#fff);background:var(--dxw-accent,#1a73e8);cursor:pointer;font:600 12px system-ui,sans-serif;";
22950
+ actions.append(cancel, submit);
22951
+ form.appendChild(actions);
22952
+ backdrop.appendChild(form);
22953
+ doc.body.appendChild(backdrop);
22954
+ let finished = false;
22955
+ const finish = (value) => {
22956
+ if (finished) return;
22957
+ finished = true;
22958
+ doc.removeEventListener("keydown", onKeyDown, true);
22959
+ backdrop.remove();
22960
+ previousFocus?.focus({ preventScroll: true });
22961
+ resolve(value);
22962
+ };
22963
+ const onKeyDown = (event) => {
22964
+ if (event.key === "Escape") {
22965
+ event.preventDefault();
22966
+ finish(void 0);
22967
+ }
22968
+ };
22969
+ doc.addEventListener("keydown", onKeyDown, true);
22970
+ backdrop.addEventListener("mousedown", (event) => {
22971
+ if (event.target === backdrop) finish(void 0);
22972
+ });
22973
+ cancel.addEventListener("click", () => finish(void 0));
22974
+ form.addEventListener("submit", (event) => {
22975
+ event.preventDefault();
22976
+ if (noFill?.checked) {
22977
+ finish(null);
22978
+ return;
22979
+ }
22980
+ const color = /^#?([0-9a-f]{6})$/i.exec(field.value.trim());
22981
+ if (!color) {
22982
+ form.reportValidity();
22983
+ return;
22984
+ }
22985
+ finish(`#${color[1].toUpperCase()}`);
22986
+ });
22987
+ requestAnimationFrame(() => {
22988
+ (noFill?.checked ? noFill : field).focus({ preventScroll: true });
22989
+ if (!noFill?.checked) field.select();
22990
+ });
22991
+ });
22526
22992
  }
22527
- function pictureGraphic(relId, id, cx, cy, webVideo) {
22528
- const blipChildren = webVideo ? [
22529
- el8("a:extLst", {}, [
22530
- el8("a:ext", { uri: "{9E29B28B-C9C2-4B96-BA92-6C9A3D8F4A6A}" }, [
22531
- el8("wp15:webVideoPr", {
22532
- "xmlns:wp15": NS_WP15,
22533
- embeddedHtml: webVideo,
22534
- w: "640",
22535
- h: "360"
22536
- })
22537
- ])
22538
- ])
22539
- ] : [];
22540
- return el8("a:graphic", { "xmlns:a": NS_A4 }, [
22541
- el8("a:graphicData", { uri: NS_PIC2 }, [
22542
- el8("pic:pic", { "xmlns:pic": NS_PIC2 }, [
22543
- el8("pic:nvPicPr", {}, [
22544
- el8("pic:cNvPr", { id, name: `Picture ${id}` }),
22545
- el8("pic:cNvPicPr", {}, [el8("a:picLocks", { noChangeAspect: "1" })])
22546
- ]),
22547
- el8("pic:blipFill", {}, [
22548
- el8("a:blip", { "xmlns:r": NS_R3, "r:embed": relId }, blipChildren),
22549
- el8("a:stretch", {}, [el8("a:fillRect")])
22550
- ]),
22551
- el8("pic:spPr", {}, [
22552
- el8("a:xfrm", {}, [el8("a:off", { x: "0", y: "0" }), el8("a:ext", { cx, cy })]),
22553
- el8("a:prstGeom", { prst: "rect" }, [el8("a:avLst")])
22554
- ])
22993
+ function requestLineStyleDialog(anchor, value, titleText = "Line style") {
22994
+ const doc = anchor.ownerDocument;
22995
+ const win = doc.defaultView;
22996
+ return new Promise((resolve) => {
22997
+ const fieldName = titleText === "Outline" ? "Outline" : "Line";
22998
+ const id = `dxw-line-style-dialog-${++dialogSequence}`;
22999
+ const previousFocus = doc.activeElement instanceof HTMLElement ? doc.activeElement : null;
23000
+ const backdrop = doc.createElement("div");
23001
+ backdrop.className = "dxw-input-dialog-backdrop";
23002
+ backdrop.dataset.dxwLineStyleDialog = "";
23003
+ backdrop.style.cssText = "position:fixed;inset:0;z-index:var(--dxw-dialog-z-index,1000);display:grid;place-items:center;padding:16px;box-sizing:border-box;background:var(--dxw-dialog-backdrop,rgba(32,33,36,.38));";
23004
+ if (win) {
23005
+ const computed = win.getComputedStyle(anchor);
23006
+ for (const property of [
23007
+ "--dxw-toolbar-fg",
23008
+ "--dxw-toolbar-border",
23009
+ "--dxw-toolbar-muted",
23010
+ "--dxw-accent",
23011
+ "--dxw-accent-fg",
23012
+ "--dxw-popover-bg",
23013
+ "--dxw-popover-shadow",
23014
+ "--dxw-dialog-backdrop",
23015
+ "--dxw-dialog-z-index",
23016
+ "--dxw-dialog-width",
23017
+ "--dxw-dialog-radius",
23018
+ "--dxw-dialog-field-bg"
23019
+ ]) {
23020
+ const token = computed.getPropertyValue(property);
23021
+ if (token) backdrop.style.setProperty(property, token);
23022
+ }
23023
+ }
23024
+ const form = doc.createElement("form");
23025
+ form.className = "dxw-input-dialog dxw-line-style-dialog";
23026
+ form.setAttribute("role", "dialog");
23027
+ form.setAttribute("aria-modal", "true");
23028
+ form.setAttribute("aria-labelledby", id);
23029
+ form.style.cssText = "width:min(var(--dxw-dialog-width,420px),calc(100vw - 32px));box-sizing:border-box;display:grid;gap:12px;padding:16px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:var(--dxw-dialog-radius,10px);color:var(--dxw-toolbar-fg,#3c4043);background:var(--dxw-popover-bg,#fff);box-shadow:var(--dxw-popover-shadow,0 8px 28px rgba(0,0,0,.24));font:13px system-ui,sans-serif;";
23030
+ const title = doc.createElement("strong");
23031
+ title.id = id;
23032
+ title.className = "dxw-input-dialog-title";
23033
+ title.textContent = titleText;
23034
+ title.style.cssText = "font-size:15px;line-height:1.35;";
23035
+ form.appendChild(title);
23036
+ const fieldStyle = "width:100%;min-height:34px;box-sizing:border-box;padding:7px 9px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:6px;outline:none;color:var(--dxw-toolbar-fg,#3c4043);background:var(--dxw-dialog-field-bg,var(--dxw-popover-bg,#fff));font:13px system-ui,sans-serif;";
23037
+ const labelStyle = "display:grid;gap:5px;color:var(--dxw-toolbar-muted,#5f6368);font-size:12px;";
23038
+ const colorLabel = doc.createElement("label");
23039
+ colorLabel.textContent = "Color";
23040
+ colorLabel.style.cssText = labelStyle;
23041
+ const colorRow = doc.createElement("span");
23042
+ colorRow.style.cssText = "display:grid;grid-template-columns:42px 1fr;gap:7px;";
23043
+ const colorPicker = doc.createElement("input");
23044
+ colorPicker.type = "color";
23045
+ colorPicker.value = value.color;
23046
+ colorPicker.setAttribute("aria-label", `${fieldName} color picker`);
23047
+ colorPicker.style.cssText = "width:42px;height:34px;box-sizing:border-box;padding:2px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:6px;background:var(--dxw-dialog-field-bg,var(--dxw-popover-bg,#fff));";
23048
+ const colorField = doc.createElement("input");
23049
+ colorField.type = "text";
23050
+ colorField.value = value.color.toUpperCase();
23051
+ colorField.required = true;
23052
+ colorField.pattern = "#?[0-9a-fA-F]{6}";
23053
+ colorField.setAttribute("aria-label", `${fieldName} color`);
23054
+ colorField.style.cssText = fieldStyle;
23055
+ colorPicker.addEventListener("input", () => {
23056
+ colorField.value = colorPicker.value.toUpperCase();
23057
+ });
23058
+ colorField.addEventListener("input", () => {
23059
+ const match = /^#?([0-9a-f]{6})$/i.exec(colorField.value.trim());
23060
+ if (match) colorPicker.value = `#${match[1]}`;
23061
+ });
23062
+ colorRow.append(colorPicker, colorField);
23063
+ colorLabel.appendChild(colorRow);
23064
+ form.appendChild(colorLabel);
23065
+ const widthLabel = doc.createElement("label");
23066
+ widthLabel.textContent = "Width (pixels)";
23067
+ widthLabel.style.cssText = labelStyle;
23068
+ const widthField = doc.createElement("input");
23069
+ widthField.type = "number";
23070
+ widthField.min = "0.25";
23071
+ widthField.step = "any";
23072
+ widthField.required = true;
23073
+ widthField.value = String(Number(value.width.toFixed(2)));
23074
+ widthField.setAttribute("aria-label", `${fieldName} width in pixels`);
23075
+ widthField.style.cssText = fieldStyle;
23076
+ widthLabel.appendChild(widthField);
23077
+ form.appendChild(widthLabel);
23078
+ const styleLabel = doc.createElement("label");
23079
+ styleLabel.textContent = "Style";
23080
+ styleLabel.style.cssText = labelStyle;
23081
+ const styleField = doc.createElement("select");
23082
+ styleField.setAttribute("aria-label", `${fieldName} style`);
23083
+ styleField.style.cssText = fieldStyle;
23084
+ for (const [style, label] of [["solid", "Solid"], ["dashed", "Dashed"], ["dotted", "Dotted"]]) {
23085
+ const option = doc.createElement("option");
23086
+ option.value = style;
23087
+ option.textContent = label;
23088
+ option.selected = value.style === style;
23089
+ styleField.appendChild(option);
23090
+ }
23091
+ styleLabel.appendChild(styleField);
23092
+ form.appendChild(styleLabel);
23093
+ const actions = doc.createElement("div");
23094
+ actions.className = "dxw-input-dialog-actions";
23095
+ actions.style.cssText = "display:flex;justify-content:flex-end;gap:7px;";
23096
+ const cancel = doc.createElement("button");
23097
+ cancel.type = "button";
23098
+ cancel.className = "dxw-input-dialog-cancel";
23099
+ cancel.textContent = "Cancel";
23100
+ cancel.style.cssText = "min-height:32px;padding:0 12px;border:1px solid var(--dxw-toolbar-border,#dadce0);border-radius:7px;color:var(--dxw-toolbar-fg,#3c4043);background:var(--dxw-popover-bg,#fff);cursor:pointer;font:600 12px system-ui,sans-serif;";
23101
+ const submit = doc.createElement("button");
23102
+ submit.type = "submit";
23103
+ submit.className = "dxw-input-dialog-submit";
23104
+ submit.textContent = "Apply";
23105
+ submit.style.cssText = "min-height:32px;padding:0 14px;border:1px solid transparent;border-radius:7px;color:var(--dxw-accent-fg,#fff);background:var(--dxw-accent,#1a73e8);cursor:pointer;font:600 12px system-ui,sans-serif;";
23106
+ actions.append(cancel, submit);
23107
+ form.appendChild(actions);
23108
+ backdrop.appendChild(form);
23109
+ doc.body.appendChild(backdrop);
23110
+ let finished = false;
23111
+ const finish = (result) => {
23112
+ if (finished) return;
23113
+ finished = true;
23114
+ doc.removeEventListener("keydown", onKeyDown, true);
23115
+ backdrop.remove();
23116
+ previousFocus?.focus({ preventScroll: true });
23117
+ resolve(result);
23118
+ };
23119
+ const onKeyDown = (event) => {
23120
+ if (event.key === "Escape") {
23121
+ event.preventDefault();
23122
+ finish(null);
23123
+ }
23124
+ };
23125
+ doc.addEventListener("keydown", onKeyDown, true);
23126
+ backdrop.addEventListener("mousedown", (event) => {
23127
+ if (event.target === backdrop) finish(null);
23128
+ });
23129
+ cancel.addEventListener("click", () => finish(null));
23130
+ form.addEventListener("submit", (event) => {
23131
+ event.preventDefault();
23132
+ const color = /^#?([0-9a-f]{6})$/i.exec(colorField.value.trim());
23133
+ const width = Number(widthField.value);
23134
+ if (!color || !Number.isFinite(width) || width <= 0) {
23135
+ form.reportValidity();
23136
+ return;
23137
+ }
23138
+ finish({
23139
+ color: `#${color[1].toUpperCase()}`,
23140
+ width,
23141
+ style: styleField.value
23142
+ });
23143
+ });
23144
+ requestAnimationFrame(() => {
23145
+ colorField.focus({ preventScroll: true });
23146
+ colorField.select();
23147
+ });
23148
+ });
23149
+ }
23150
+
23151
+ // ../core/src/edit/objects.ts
23152
+ var EMU_PER_PX6 = 9525;
23153
+ var NS_WP5 = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing";
23154
+ var NS_A4 = "http://schemas.openxmlformats.org/drawingml/2006/main";
23155
+ var NS_PIC2 = "http://schemas.openxmlformats.org/drawingml/2006/picture";
23156
+ var NS_R3 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
23157
+ var NS_MC = "http://schemas.openxmlformats.org/markup-compatibility/2006";
23158
+ var NS_AM3D = "http://schemas.microsoft.com/office/drawing/2017/model3d";
23159
+ var NS_WP15 = "http://schemas.microsoft.com/office/word/2012/wordprocessingDrawing";
23160
+ function el8(name, attrs = {}, children2 = [], text = "") {
23161
+ return { name, attrs, children: children2, text };
23162
+ }
23163
+ function prefixOf9(node) {
23164
+ return node.name.includes(":") ? node.name.slice(0, node.name.indexOf(":") + 1) : "";
23165
+ }
23166
+ function insertAfterCaretRun(doc, caretT, content) {
23167
+ const caretRun = doc.findParentOf(caretT);
23168
+ const parent = caretRun && doc.findParentOf(caretRun);
23169
+ if (!caretRun || !parent || localName(caretRun.name) !== "r") return false;
23170
+ const w = prefixOf9(caretRun);
23171
+ parent.children.splice(parent.children.indexOf(caretRun) + 1, 0, el8(`${w}r`, {}, [content]));
23172
+ doc.refresh();
23173
+ return true;
23174
+ }
23175
+ function pictureGraphic(relId, id, cx, cy, webVideo) {
23176
+ const blipChildren = webVideo ? [
23177
+ el8("a:extLst", {}, [
23178
+ el8("a:ext", { uri: "{9E29B28B-C9C2-4B96-BA92-6C9A3D8F4A6A}" }, [
23179
+ el8("wp15:webVideoPr", {
23180
+ "xmlns:wp15": NS_WP15,
23181
+ embeddedHtml: webVideo,
23182
+ w: "640",
23183
+ h: "360"
23184
+ })
23185
+ ])
23186
+ ])
23187
+ ] : [];
23188
+ return el8("a:graphic", { "xmlns:a": NS_A4 }, [
23189
+ el8("a:graphicData", { uri: NS_PIC2 }, [
23190
+ el8("pic:pic", { "xmlns:pic": NS_PIC2 }, [
23191
+ el8("pic:nvPicPr", {}, [
23192
+ el8("pic:cNvPr", { id, name: `Picture ${id}` }),
23193
+ el8("pic:cNvPicPr", {}, [el8("a:picLocks", { noChangeAspect: "1" })])
23194
+ ]),
23195
+ el8("pic:blipFill", {}, [
23196
+ el8("a:blip", { "xmlns:r": NS_R3, "r:embed": relId }, blipChildren),
23197
+ el8("a:stretch", {}, [el8("a:fillRect")])
23198
+ ]),
23199
+ el8("pic:spPr", {}, [
23200
+ el8("a:xfrm", {}, [el8("a:off", { x: "0", y: "0" }), el8("a:ext", { cx, cy })]),
23201
+ el8("a:prstGeom", { prst: "rect" }, [el8("a:avLst")])
23202
+ ])
22555
23203
  ])
22556
23204
  ])
22557
23205
  ]);
@@ -23398,6 +24046,19 @@ function textElements(el16) {
23398
24046
  walk2(el16);
23399
24047
  return out;
23400
24048
  }
24049
+ function clearListParagraphFormatting(doc, paragraph) {
24050
+ const pPr = paragraph.children.find((child4) => localName(child4.name) === "pPr");
24051
+ if (!pPr) return false;
24052
+ const style = pPr.children.find((child4) => localName(child4.name) === "pStyle");
24053
+ const styleHasNumbering = !!resolveParagraphStyleChain(doc.styles, attr(style, "val"), false).pPr.numbering;
24054
+ const hasDirectNumbering = pPr.children.some((child4) => localName(child4.name) === "numPr");
24055
+ if (!hasDirectNumbering && !styleHasNumbering) return false;
24056
+ pPr.children = pPr.children.filter((child4) => {
24057
+ const name = localName(child4.name);
24058
+ return name !== "numPr" && !(name === "pStyle" && styleHasNumbering);
24059
+ });
24060
+ return true;
24061
+ }
23401
24062
  function emptyParagraphLike(el16) {
23402
24063
  const prefix = el16.name.includes(":") ? el16.name.slice(0, el16.name.indexOf(":") + 1) : "";
23403
24064
  const text = {
@@ -23569,6 +24230,7 @@ var _DocxEditor = class _DocxEditor {
23569
24230
  this.onGripMouseDown = (e) => {
23570
24231
  const target = e.target;
23571
24232
  if (this.wordArtTextEditor?.input.contains(target)) return;
24233
+ if (this.smartArtTextEditor?.input.contains(target)) return;
23572
24234
  if (this.startInkGroupInteraction(e, target)) return;
23573
24235
  if (this.startInkInteraction(e, target)) return;
23574
24236
  if (this.startCheckboxInteraction(e, target)) return;
@@ -23767,9 +24429,11 @@ var _DocxEditor = class _DocxEditor {
23767
24429
  this.selectedInkGroup = null;
23768
24430
  // ---------- images: select, resize, drag-move ----------
23769
24431
  this.selectedImage = null;
24432
+ this.selectedSmartArtNodeIndex = null;
23770
24433
  this.imageOverlay = null;
23771
24434
  this.imageToolbar = null;
23772
24435
  this.wordArtTextEditor = null;
24436
+ this.smartArtTextEditor = null;
23773
24437
  /** First press selects a model; subsequent drags belong to its 3D viewport. */
23774
24438
  this.onModel3DPointerDown = (event) => {
23775
24439
  const binding = this.model3DBinding(event.target);
@@ -23816,6 +24480,7 @@ var _DocxEditor = class _DocxEditor {
23816
24480
  if (this.imageOverlay?.contains(e.target) || this.imageToolbar?.contains(e.target)) return;
23817
24481
  if (this.wordArtOverlay?.contains(e.target)) return;
23818
24482
  if (this.wordArtTextEditor?.input.contains(e.target)) return;
24483
+ if (this.smartArtTextEditor?.input.contains(e.target)) return;
23819
24484
  if (this.selectedInkGroup?.overlay.contains(e.target)) return;
23820
24485
  if (e.target?.closest?.("[data-dxw-model3d-viewer]")) return;
23821
24486
  this.deselectInkGroup();
@@ -24081,6 +24746,18 @@ var _DocxEditor = class _DocxEditor {
24081
24746
  this.exitHeaderFooter();
24082
24747
  return;
24083
24748
  }
24749
+ if (this.selectedImage && this.selectedSmartArtNodeIndex !== null && !e.metaKey && !e.ctrlKey && !e.altKey) {
24750
+ if (e.key === "Enter") {
24751
+ e.preventDefault();
24752
+ this.editSmartArtNodeText(this.selectedImage.src, this.selectedSmartArtNodeIndex);
24753
+ return;
24754
+ }
24755
+ if (e.key.length === 1) {
24756
+ e.preventDefault();
24757
+ this.editSmartArtNodeText(this.selectedImage.src, this.selectedSmartArtNodeIndex, e.key);
24758
+ return;
24759
+ }
24760
+ }
24084
24761
  if (this.caret && (e.key === "Backspace" || e.key === "Delete" || e.key.startsWith("Arrow"))) {
24085
24762
  this.positionCaret();
24086
24763
  }
@@ -24916,6 +25593,217 @@ var _DocxEditor = class _DocxEditor {
24916
25593
  getSelectedDrawingSource() {
24917
25594
  return this.selectedImage?.src ?? null;
24918
25595
  }
25596
+ getSelectedChartData() {
25597
+ const src = this.selectedImage?.src;
25598
+ if (!src) return null;
25599
+ return this.host.getHandle()?.drawings.find((binding) => binding.item.src === src)?.item.chartData ?? null;
25600
+ }
25601
+ getSelectedSmartArtData() {
25602
+ const src = this.selectedImage?.src;
25603
+ if (!src) return null;
25604
+ return this.host.getHandle()?.drawings.find((binding) => binding.item.src === src)?.item.smartArtData ?? null;
25605
+ }
25606
+ getSelectedSmartArtTextFormat() {
25607
+ const src = this.selectedImage?.src;
25608
+ if (!src || this.getSelectedObjectContext()?.kind !== "smartArt") return null;
25609
+ return smartArtTextFormat(this.host.doc, src, this.selectedSmartArtNodeIndex ?? 0);
25610
+ }
25611
+ setSelectedSmartArtTextFormat(patch) {
25612
+ const src = this.selectedImage?.src;
25613
+ if (!src || this.getSelectedObjectContext()?.kind !== "smartArt") return false;
25614
+ const current = smartArtTextFormat(this.host.doc, src, this.selectedSmartArtNodeIndex ?? 0);
25615
+ if (!current) return false;
25616
+ const nodeIndex = this.selectedSmartArtNodeIndex;
25617
+ this.host.history?.checkpoint();
25618
+ if (!setSmartArtTextFormat(this.host.doc, src, { ...current, ...patch }, nodeIndex ?? void 0)) return false;
25619
+ this.host.rerender();
25620
+ this.reselectImage(src, void 0, nodeIndex);
25621
+ return true;
25622
+ }
25623
+ getSelectedObjectContext() {
25624
+ const selected = this.selectedImage;
25625
+ if (!selected) return null;
25626
+ if (selected.kind === "image") {
25627
+ return { kind: selected.el.dataset.dxwModel3d ? "model3d" : "image", canEditText: false };
25628
+ }
25629
+ const binding = this.host.getHandle()?.drawings.find((item) => item.item.src === selected.src);
25630
+ if (binding?.item.smartArtData) {
25631
+ return {
25632
+ kind: "smartArt",
25633
+ canEditText: this.selectedSmartArtNodeIndex !== null,
25634
+ smartArtNodeSelected: this.selectedSmartArtNodeIndex !== null,
25635
+ smartArtNodeIndex: this.selectedSmartArtNodeIndex ?? void 0
25636
+ };
25637
+ }
25638
+ if (binding?.item.chartData) return { kind: "chart", canEditText: false };
25639
+ const kind = isDrawingLine(selected.src) ? "line" : "shape";
25640
+ return {
25641
+ kind,
25642
+ canEditText: kind === "shape" && !!binding?.item.textboxStory && !isDrawingWordArt(selected.src)
25643
+ };
25644
+ }
25645
+ runSelectedObjectCommand(command) {
25646
+ const selected = this.selectedImage;
25647
+ const context = this.getSelectedObjectContext();
25648
+ if (!selected || !context) return false;
25649
+ const { src, el: el16 } = selected;
25650
+ const selectedSmartArtNodeIndex = this.selectedSmartArtNodeIndex;
25651
+ const reselect = () => this.reselectImage(src, void 0, selectedSmartArtNodeIndex);
25652
+ if (command === "delete") {
25653
+ this.deleteSelectedImage();
25654
+ return true;
25655
+ }
25656
+ if (command === "bringForward" || command === "sendBackward") {
25657
+ return this.arrangeSelectedObject(command === "bringForward" ? "bringToFront" : "sendToBack");
25658
+ }
25659
+ if (command.startsWith("wrap")) {
25660
+ const mode = command === "wrapInline" ? "inline" : command === "wrapSquare" ? "square" : command === "wrapTopAndBottom" ? "topAndBottom" : command === "wrapBehind" ? "behind" : "none";
25661
+ this.host.history?.checkpoint();
25662
+ const handle = this.host.getHandle();
25663
+ const binding = selected.kind === "image" ? handle?.images.find((item) => item.item.src === src) : handle?.drawings.find((item) => item.item.src === src);
25664
+ const marginLeft = this.host.doc.sections[0]?.props.marginLeft ?? 96;
25665
+ const position = binding ? { x: binding.item.x - marginLeft, y: 0 } : void 0;
25666
+ if (!setImageWrap(this.host.doc, src, mode, position)) return false;
25667
+ this.host.rerender(void 0, "global");
25668
+ reselect();
25669
+ return true;
25670
+ }
25671
+ if (command === "size") {
25672
+ void requestNumberPairDialog(this.host.container, {
25673
+ title: "Exact size",
25674
+ firstLabel: "Width (pixels)",
25675
+ secondLabel: "Height (pixels)",
25676
+ value: {
25677
+ first: Math.round(parseFloat(el16.style.width) || 0),
25678
+ second: Math.round(parseFloat(el16.style.height) || 0)
25679
+ },
25680
+ min: 1,
25681
+ step: 1
25682
+ }).then((next) => {
25683
+ if (!next) return;
25684
+ this.host.history?.checkpoint();
25685
+ if (resizeDrawing(this.host.doc, src, next.first, next.second)) {
25686
+ this.host.rerender();
25687
+ reselect();
25688
+ }
25689
+ });
25690
+ return true;
25691
+ }
25692
+ if (command === "position") {
25693
+ void requestNumberPairDialog(this.host.container, {
25694
+ title: "Page position",
25695
+ firstLabel: "X (pixels)",
25696
+ secondLabel: "Y (pixels)",
25697
+ value: {
25698
+ first: Math.round(parseFloat(el16.style.left) || 0),
25699
+ second: Math.round(parseFloat(el16.style.top) || 0)
25700
+ },
25701
+ step: 1
25702
+ }).then((next) => {
25703
+ if (!next) return;
25704
+ this.host.history?.checkpoint();
25705
+ if (!isFloatingDrawing(src)) setImageWrap(this.host.doc, src, "square", { x: 0, y: 0 });
25706
+ if (setFloatingPagePosition(this.host.doc, src, next.first, next.second)) {
25707
+ this.host.rerender();
25708
+ reselect();
25709
+ }
25710
+ });
25711
+ return true;
25712
+ }
25713
+ if (command === "rotate") {
25714
+ if (!["shape", "line", "image", "model3d"].includes(context.kind)) return false;
25715
+ void requestTextInputDialog(this.host.container, {
25716
+ title: "Rotation",
25717
+ label: "Degrees clockwise",
25718
+ value: String(drawingRotation(src)),
25719
+ inputType: "number",
25720
+ step: 1
25721
+ }).then((next) => {
25722
+ if (next === null) return;
25723
+ const degrees = parseFloat(next);
25724
+ if (!Number.isFinite(degrees)) return;
25725
+ this.host.history?.checkpoint();
25726
+ if (setDrawingRotation(this.host.doc, src, degrees)) {
25727
+ this.host.rerender(void 0, "global");
25728
+ reselect();
25729
+ }
25730
+ });
25731
+ return true;
25732
+ }
25733
+ if (command === "altText") {
25734
+ if (context.kind !== "image" && context.kind !== "model3d") return false;
25735
+ void requestTextInputDialog(this.host.container, {
25736
+ title: "Alternative text",
25737
+ label: "Describe this object for screen readers",
25738
+ value: imageAltText(src)
25739
+ }).then((next) => {
25740
+ if (next === null) return;
25741
+ this.host.history?.checkpoint();
25742
+ if (setImageAltText(this.host.doc, src, next)) {
25743
+ this.host.rerender();
25744
+ reselect();
25745
+ }
25746
+ });
25747
+ return true;
25748
+ }
25749
+ if (command === "editText") {
25750
+ if (!context.canEditText) return false;
25751
+ if (context.kind === "smartArt" && selectedSmartArtNodeIndex !== null) {
25752
+ this.editSmartArtNodeText(src, selectedSmartArtNodeIndex);
25753
+ return true;
25754
+ }
25755
+ const binding = this.host.getHandle()?.drawings.find((item) => item.item.src === src);
25756
+ if (!binding) return false;
25757
+ const rect = binding.el.getBoundingClientRect();
25758
+ this.enterTextboxStory(src, rect.left + rect.width / 2, rect.top + rect.height / 2);
25759
+ return true;
25760
+ }
25761
+ if (command === "fill") {
25762
+ if (context.kind !== "shape" && context.kind !== "smartArt") return false;
25763
+ void requestColorDialog(this.host.container, {
25764
+ title: "Fill",
25765
+ value: context.kind === "smartArt" ? smartArtFillColor(this.host.doc, src, selectedSmartArtNodeIndex ?? 0) : drawingFillColor(src),
25766
+ allowNone: true
25767
+ }).then((next) => {
25768
+ if (next === void 0) return;
25769
+ this.host.history?.checkpoint();
25770
+ const changed = context.kind === "smartArt" ? setSmartArtFill(this.host.doc, src, next, selectedSmartArtNodeIndex ?? void 0) : setDrawingFill(this.host.doc, src, next);
25771
+ if (changed) {
25772
+ this.host.rerender();
25773
+ reselect();
25774
+ }
25775
+ });
25776
+ return true;
25777
+ }
25778
+ if (command === "outline" || command === "lineStyle") {
25779
+ if (command === "outline" && context.kind !== "shape" || command === "lineStyle" && context.kind !== "line") return false;
25780
+ const style = drawingLineStyle(src);
25781
+ if (!style) return false;
25782
+ const title = command === "lineStyle" ? "Line style" : "Outline";
25783
+ void requestLineStyleDialog(this.host.container, {
25784
+ color: style.color,
25785
+ width: style.width,
25786
+ style: style.dash
25787
+ }, title).then((next) => {
25788
+ if (!next) return;
25789
+ this.host.history?.checkpoint();
25790
+ if (setDrawingLineStyle(this.host.doc, src, next.color, next.width, next.style)) {
25791
+ this.host.rerender();
25792
+ reselect();
25793
+ }
25794
+ });
25795
+ return true;
25796
+ }
25797
+ if (command === "reset3d") {
25798
+ if (context.kind !== "model3d") return false;
25799
+ this.host.history?.checkpoint();
25800
+ if (!setModel3DRotation(this.host.doc, src, { x: 0, y: 0, z: 0 })) return false;
25801
+ this.host.rerender(void 0, "local");
25802
+ reselect();
25803
+ return true;
25804
+ }
25805
+ return false;
25806
+ }
24919
25807
  /** Whether Layout's contextual object commands currently have a target. */
24920
25808
  hasSelectedObject() {
24921
25809
  return this.selectedImage !== null || this.selectedInkGroup !== null;
@@ -25668,18 +26556,20 @@ var _DocxEditor = class _DocxEditor {
25668
26556
  deselectImage() {
25669
26557
  const changed = this.selectedImage !== null;
25670
26558
  this.wordArtTextEditor?.finish(true, false);
26559
+ this.smartArtTextEditor?.finish(true, false);
25671
26560
  this.imageOverlay?.remove();
25672
26561
  this.imageToolbar?.remove();
25673
26562
  this.imageOverlay = null;
25674
26563
  this.imageToolbar = null;
25675
26564
  this.selectedImage = null;
26565
+ this.selectedSmartArtNodeIndex = null;
25676
26566
  if (changed) this.notifyObjectSelection();
25677
26567
  }
25678
26568
  /** Re-select the same drawing after a re-render replaced its element.
25679
26569
  * A header/footer drawing paints one instance per page from the same src;
25680
26570
  * `near` (client px) picks the instance the user is actually working on
25681
26571
  * instead of the first page's copy. */
25682
- reselectImage(src, near) {
26572
+ reselectImage(src, near, smartArtNodeIndex = this.selectedSmartArtNodeIndex) {
25683
26573
  const handle = this.host.getHandle();
25684
26574
  if (!handle) return;
25685
26575
  const img = nearestInstance(handle.images.filter((b) => b.item.src === src), near);
@@ -25688,7 +26578,7 @@ var _DocxEditor = class _DocxEditor {
25688
26578
  return;
25689
26579
  }
25690
26580
  const drw = nearestInstance(handle.drawings.filter((b) => b.item.src === src), near);
25691
- if (drw) this.selectImage(drw.el, src, void 0, "drawing");
26581
+ if (drw) this.selectImage(drw.el, src, void 0, "drawing", smartArtNodeIndex);
25692
26582
  }
25693
26583
  static handleCursor(dir) {
25694
26584
  if (dir === "nw" || dir === "se") return "nwse-resize";
@@ -25696,11 +26586,12 @@ var _DocxEditor = class _DocxEditor {
25696
26586
  if (dir === "n" || dir === "s") return "ns-resize";
25697
26587
  return "ew-resize";
25698
26588
  }
25699
- selectImage(el16, src, item, kind = "image") {
26589
+ selectImage(el16, src, _item, kind = "image", smartArtNodeIndex = null) {
25700
26590
  this.deselectInkGroup();
25701
26591
  this.deselectImage();
25702
26592
  this.hideCaret();
25703
26593
  this.selectedImage = { el: el16, src, kind };
26594
+ this.selectedSmartArtNodeIndex = smartArtNodeIndex;
25704
26595
  const overlay = document.createElement("div");
25705
26596
  overlay.dataset.dxwObjectSelection = "1";
25706
26597
  overlay.style.position = "absolute";
@@ -25714,6 +26605,15 @@ var _DocxEditor = class _DocxEditor {
25714
26605
  overlay.style.boxSizing = "border-box";
25715
26606
  overlay.style.pointerEvents = "none";
25716
26607
  overlay.style.zIndex = "2147483647";
26608
+ if (smartArtNodeIndex !== null) {
26609
+ const node = this.host.getHandle()?.drawings.find((binding) => binding.item.src === src && binding.el === el16)?.item.smartArtNodes?.find((candidate) => candidate.index === smartArtNodeIndex);
26610
+ if (node) {
26611
+ const nodeOutline = document.createElement("div");
26612
+ nodeOutline.dataset.dxwSmartArtNodeSelection = String(node.index);
26613
+ nodeOutline.style.cssText = `position:absolute;left:${node.x}px;top:${node.y}px;width:${node.width}px;height:${node.height}px;box-sizing:border-box;border:2px solid #1a73e8;border-radius:3px;background:rgba(26,115,232,.08);`;
26614
+ overlay.appendChild(nodeOutline);
26615
+ }
26616
+ }
25717
26617
  const line2 = kind === "drawing" && isDrawingLine(src);
25718
26618
  if (line2) {
25719
26619
  const move = document.createElement("div");
@@ -25763,15 +26663,8 @@ var _DocxEditor = class _DocxEditor {
25763
26663
  });
25764
26664
  b.addEventListener("click", (ce) => {
25765
26665
  ce.stopPropagation();
25766
- this.host.history?.checkpoint();
25767
- const sp = this.host.doc.sections[0]?.props;
25768
- const pos = item ? { x: item.x - (sp?.marginLeft ?? 96), y: 0 } : void 0;
25769
- if (setImageWrap(this.host.doc, src, mode, pos)) {
25770
- this.host.rerender();
25771
- this.reselectImage(src);
25772
- } else {
25773
- this.deselectImage();
25774
- }
26666
+ const command = mode === "inline" ? "wrapInline" : mode === "square" ? "wrapSquare" : mode === "topAndBottom" ? "wrapTopAndBottom" : mode === "behind" ? "wrapBehind" : "wrapFront";
26667
+ if (!this.runSelectedObjectCommand(command)) this.deselectImage();
25775
26668
  });
25776
26669
  bar.appendChild(b);
25777
26670
  }
@@ -25793,117 +26686,58 @@ var _DocxEditor = class _DocxEditor {
25793
26686
  });
25794
26687
  bar.appendChild(b);
25795
26688
  };
25796
- extra("Alt", "Alternative text", () => {
25797
- const cur = imageAltText(src);
25798
- void requestTextInputDialog(this.host.container, {
25799
- title: "Alternative text",
25800
- label: "Describe this object for screen readers",
25801
- value: cur
25802
- }).then((next) => {
25803
- if (next === null) return;
25804
- this.host.history?.checkpoint();
25805
- if (setImageAltText(this.host.doc, src, next)) this.host.rerender();
25806
- this.deselectImage();
26689
+ const context = this.getSelectedObjectContext();
26690
+ if (context?.kind === "image" || context?.kind === "model3d") {
26691
+ extra("Alt", "Alternative text", () => {
26692
+ this.runSelectedObjectCommand("altText");
25807
26693
  });
25808
- });
26694
+ }
25809
26695
  extra("Size", "Exact size (px)", () => {
25810
- const curW = parseFloat(el16.style.width) || 0;
25811
- const curH = parseFloat(el16.style.height) || 0;
25812
- const cur = `${Math.round(curW)}x${Math.round(curH)}`;
25813
- void requestTextInputDialog(this.host.container, {
25814
- title: "Exact size",
25815
- label: "Width \xD7 height in pixels",
25816
- value: cur,
25817
- placeholder: "640 \xD7 360"
25818
- }).then((next) => {
25819
- const match = next && /^\s*(\d+)\s*[x×]\s*(\d+)\s*$/i.exec(next);
25820
- if (!match) return;
25821
- this.host.history?.checkpoint();
25822
- if (resizeDrawing(this.host.doc, src, parseInt(match[1], 10), parseInt(match[2], 10))) this.host.rerender();
25823
- this.deselectImage();
25824
- });
26696
+ this.runSelectedObjectCommand("size");
25825
26697
  });
25826
26698
  extra("Position", "Exact page position (px)", () => {
25827
- const curX = parseFloat(el16.style.left) || 0;
25828
- const curY = parseFloat(el16.style.top) || 0;
25829
- void requestTextInputDialog(this.host.container, {
25830
- title: "Page position",
25831
- label: "X, Y in pixels",
25832
- value: `${Math.round(curX)}, ${Math.round(curY)}`,
25833
- placeholder: "120, 80"
25834
- }).then((next) => {
25835
- const match = next && /^\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/.exec(next);
25836
- if (!match) return;
25837
- this.host.history?.checkpoint();
25838
- if (!isFloatingDrawing(src)) setImageWrap(this.host.doc, src, "square", { x: 0, y: 0 });
25839
- if (setFloatingPagePosition(this.host.doc, src, parseFloat(match[1]), parseFloat(match[2]))) {
25840
- this.host.rerender();
25841
- this.reselectImage(src);
25842
- }
25843
- });
26699
+ this.runSelectedObjectCommand("position");
25844
26700
  });
26701
+ const drawingBinding = kind === "drawing" ? this.host.getHandle()?.drawings.find((binding) => binding.item.src === src) : void 0;
25845
26702
  if (kind === "drawing") {
25846
- extra("Rotate", "Set rotation (degrees)", () => {
25847
- void requestTextInputDialog(this.host.container, {
25848
- title: "Rotation",
25849
- label: "Degrees clockwise",
25850
- value: String(drawingRotation(src)),
25851
- placeholder: "90"
25852
- }).then((next) => {
25853
- if (next === null) return;
25854
- const degrees = parseFloat(next);
25855
- if (!Number.isFinite(degrees)) return;
25856
- this.host.history?.checkpoint();
25857
- if (setDrawingRotation(this.host.doc, src, degrees)) {
25858
- this.host.rerender(void 0, "global");
25859
- this.reselectImage(src);
25860
- }
26703
+ const drawingIsLine = isDrawingLine(src);
26704
+ if (context?.kind === "shape" || context?.kind === "line") {
26705
+ extra("Rotate", "Set rotation (degrees)", () => {
26706
+ this.runSelectedObjectCommand("rotate");
25861
26707
  });
25862
- });
25863
- if (!isDrawingLine(src)) {
25864
- extra("Fill", "Shape fill color", () => {
25865
- void requestTextInputDialog(this.host.container, {
25866
- title: "Fill",
25867
- label: "Color or none",
25868
- value: drawingFillColor(src) ?? "none",
25869
- placeholder: "#4472C4 or none"
25870
- }).then((next) => {
25871
- if (next === null) return;
25872
- const color = /^\s*none\s*$/i.test(next) ? null : /^\s*#?([0-9a-f]{6})\s*$/i.exec(next)?.[1];
25873
- if (color === void 0) return;
25874
- this.host.history?.checkpoint();
25875
- if (setDrawingFill(this.host.doc, src, color ? `#${color}` : null)) {
25876
- this.host.rerender();
25877
- this.reselectImage(src);
26708
+ }
26709
+ if (!drawingIsLine) {
26710
+ if (context?.kind === "shape" || context?.kind === "smartArt") {
26711
+ extra(
26712
+ context.kind === "smartArt" ? context.smartArtNodeSelected ? "Node fill" : "Fill all" : "Fill",
26713
+ context.kind === "smartArt" ? context.smartArtNodeSelected ? "Selected SmartArt node fill" : "All SmartArt node fills" : "Shape fill color",
26714
+ () => {
26715
+ this.runSelectedObjectCommand("fill");
25878
26716
  }
25879
- });
25880
- });
26717
+ );
26718
+ }
25881
26719
  }
25882
- extra("Outline", "Outline color and weight", () => {
25883
- const style = drawingLineStyle(src);
25884
- if (!style) return;
25885
- void requestTextInputDialog(this.host.container, {
25886
- title: "Outline",
25887
- label: "Color and width in pixels",
25888
- value: `${style.color}, ${Number(style.width.toFixed(2))}`,
25889
- placeholder: "#1a73e8, 1.5"
25890
- }).then((next) => {
25891
- const match = next && /^\s*#?([0-9a-f]{6})\s*,\s*(\d+(?:\.\d+)?)\s*$/i.exec(next);
25892
- if (!match) return;
25893
- this.host.history?.checkpoint();
25894
- if (setDrawingLineStyle(this.host.doc, src, `#${match[1]}`, parseFloat(match[2]))) {
25895
- this.host.rerender();
25896
- this.reselectImage(src);
25897
- }
26720
+ if (context?.kind === "line") {
26721
+ extra("Line style", "Line color, weight, and style", () => {
26722
+ this.runSelectedObjectCommand("lineStyle");
26723
+ });
26724
+ } else if (context?.kind === "shape") {
26725
+ extra("Outline", "Outline color, weight, and style", () => {
26726
+ this.runSelectedObjectCommand("outline");
25898
26727
  });
26728
+ }
26729
+ extra("Delete", "Delete shape", () => {
26730
+ this.runSelectedObjectCommand("delete");
25899
26731
  });
25900
- extra("Delete", "Delete shape", () => this.deleteSelectedImage());
25901
26732
  }
25902
- const drawingBinding = kind === "drawing" ? this.host.getHandle()?.drawings.find((binding) => binding.item.src === src) : void 0;
25903
26733
  if (drawingBinding?.item.textboxStory && !isDrawingWordArt(src)) {
25904
26734
  extra("Edit text", "Edit shape text with the Home font controls", () => {
25905
- const rect = drawingBinding.el.getBoundingClientRect();
25906
- this.enterTextboxStory(src, rect.left + rect.width / 2, rect.top + rect.height / 2);
26735
+ this.runSelectedObjectCommand("editText");
26736
+ });
26737
+ }
26738
+ if (context?.kind === "smartArt" && this.selectedSmartArtNodeIndex !== null) {
26739
+ extra("Edit text", "Edit selected SmartArt node text", () => {
26740
+ this.runSelectedObjectCommand("editText");
25907
26741
  });
25908
26742
  }
25909
26743
  if (kind === "drawing" && isDrawingWordArt(src)) {
@@ -25931,11 +26765,7 @@ var _DocxEditor = class _DocxEditor {
25931
26765
  }
25932
26766
  if (el16.dataset.dxwModel3d) {
25933
26767
  extra("Reset 3D", "Reset 3D rotation", () => {
25934
- this.host.history?.checkpoint();
25935
- if (setModel3DRotation(this.host.doc, src, { x: 0, y: 0, z: 0 })) {
25936
- this.host.rerender(void 0, "local");
25937
- this.reselectImage(src);
25938
- }
26768
+ this.runSelectedObjectCommand("reset3d");
25939
26769
  });
25940
26770
  }
25941
26771
  const surface = el16.parentElement;
@@ -26094,7 +26924,9 @@ var _DocxEditor = class _DocxEditor {
26094
26924
  void requestTextInputDialog(this.host.container, {
26095
26925
  title: "Watermark rotation",
26096
26926
  label: "Degrees clockwise",
26097
- value: String(cur)
26927
+ value: String(cur),
26928
+ inputType: "number",
26929
+ step: 1
26098
26930
  }).then((next) => {
26099
26931
  if (next === null) return;
26100
26932
  const degrees = parseFloat(next);
@@ -26252,6 +27084,8 @@ var _DocxEditor = class _DocxEditor {
26252
27084
  const drawingTarget = objectMove && this.selectedImage?.kind === "drawing" ? this.selectedImage.el : target.closest?.("[data-dxw-drawing]");
26253
27085
  const handle = this.host.getHandle();
26254
27086
  let drawingBinding = drawingTarget ? handle?.drawings.find((b) => b.el === drawingTarget) : void 0;
27087
+ const smartArtNodeTarget = target.closest?.("[data-dxw-smart-art-node]");
27088
+ const smartArtNodeIndex = smartArtNodeTarget ? Number.parseInt(smartArtNodeTarget.dataset.dxwSmartArtNode ?? "", 10) : null;
26255
27089
  if (!drawingBinding && !target.closest("button")) {
26256
27090
  const pointCaret2 = this.caretFromPoint(e.clientX, e.clientY);
26257
27091
  if (!pointCaret2 || !this.inActiveRegion(pointCaret2.t)) {
@@ -26272,6 +27106,11 @@ var _DocxEditor = class _DocxEditor {
26272
27106
  e.stopPropagation();
26273
27107
  this.suppressNextMouseUp = true;
26274
27108
  if (e.detail >= 2) {
27109
+ if (binding.item.smartArtData && smartArtNodeIndex !== null && Number.isFinite(smartArtNodeIndex)) {
27110
+ this.selectImage(binding.el, binding.item.src, void 0, "drawing", smartArtNodeIndex);
27111
+ this.editSmartArtNodeText(binding.item.src, smartArtNodeIndex);
27112
+ return true;
27113
+ }
26275
27114
  if (textboxStory) {
26276
27115
  this.enterTextboxStory(binding.item.src, e.clientX, e.clientY);
26277
27116
  return true;
@@ -26295,7 +27134,7 @@ var _DocxEditor = class _DocxEditor {
26295
27134
  moved = true;
26296
27135
  if (binding.item.anchored) {
26297
27136
  if (this.selectedImage?.src !== binding.item.src) {
26298
- this.selectImage(el16, binding.item.src, void 0, "drawing");
27137
+ this.selectImage(el16, binding.item.src, void 0, "drawing", Number.isFinite(smartArtNodeIndex) ? smartArtNodeIndex : null);
26299
27138
  }
26300
27139
  toolbarLeft0 = parseFloat(this.imageToolbar?.style.left ?? "") || 0;
26301
27140
  toolbarTop0 = parseFloat(this.imageToolbar?.style.top ?? "") || 0;
@@ -26325,7 +27164,7 @@ var _DocxEditor = class _DocxEditor {
26325
27164
  this.hideDropIndicator();
26326
27165
  this.suppressNextMouseUp = false;
26327
27166
  if (!moved) {
26328
- this.selectImage(binding.el, binding.item.src, void 0, "drawing");
27167
+ this.selectImage(binding.el, binding.item.src, void 0, "drawing", Number.isFinite(smartArtNodeIndex) ? smartArtNodeIndex : null);
26329
27168
  return;
26330
27169
  }
26331
27170
  if (binding.item.anchored) {
@@ -26543,6 +27382,59 @@ var _DocxEditor = class _DocxEditor {
26543
27382
  }
26544
27383
  return false;
26545
27384
  }
27385
+ editSmartArtNodeText(src, nodeIndex, replacement) {
27386
+ if (this.smartArtTextEditor) {
27387
+ this.smartArtTextEditor.input.focus();
27388
+ this.smartArtTextEditor.input.select();
27389
+ return;
27390
+ }
27391
+ const binding = this.host.getHandle()?.drawings.find((item) => item.item.src === src);
27392
+ const node = binding?.item.smartArtNodes?.find((item) => item.index === nodeIndex);
27393
+ const surface = binding?.el.parentElement;
27394
+ const original = binding?.item.smartArtData?.items[nodeIndex];
27395
+ if (!binding || !node || !surface || original === void 0) return;
27396
+ const zoom = this.host.zoom ?? 1;
27397
+ const surfaceRect = surface.getBoundingClientRect();
27398
+ const drawingRect = binding.el.getBoundingClientRect();
27399
+ const input = document.createElement("input");
27400
+ input.dataset.dxwSmartArtNodeEditor = String(nodeIndex);
27401
+ input.value = replacement ?? original;
27402
+ input.setAttribute("aria-label", `Edit SmartArt node ${nodeIndex + 1}`);
27403
+ input.style.cssText = `position:absolute;left:${(drawingRect.left - surfaceRect.left) / zoom + node.x}px;top:${(drawingRect.top - surfaceRect.top) / zoom + node.y}px;width:${node.width}px;height:${node.height}px;box-sizing:border-box;border:2px solid #1a73e8;border-radius:4px;background:rgba(255,255,255,.96);color:#202124;font:600 14px/1.2 system-ui,sans-serif;text-align:center;padding:4px 8px;z-index:2147483647;outline:none;`;
27404
+ const near = {
27405
+ x: drawingRect.left + (node.x + node.width / 2) * zoom,
27406
+ y: drawingRect.top + (node.y + node.height / 2) * zoom
27407
+ };
27408
+ const finish = (commit, reselect) => {
27409
+ if (this.smartArtTextEditor?.input !== input) return;
27410
+ this.smartArtTextEditor = null;
27411
+ const next = input.value;
27412
+ input.remove();
27413
+ if (commit && next !== original) {
27414
+ this.host.history?.checkpoint();
27415
+ if (setSmartArtNodeText(this.host.doc, src, nodeIndex, next)) this.host.rerender();
27416
+ }
27417
+ if (reselect) this.reselectImage(src, near, nodeIndex);
27418
+ this.focusText();
27419
+ };
27420
+ this.smartArtTextEditor = { input, finish };
27421
+ input.addEventListener("mousedown", (event) => event.stopPropagation());
27422
+ input.addEventListener("keydown", (event) => {
27423
+ event.stopPropagation();
27424
+ if (event.key === "Enter") {
27425
+ event.preventDefault();
27426
+ finish(true, true);
27427
+ } else if (event.key === "Escape") {
27428
+ event.preventDefault();
27429
+ finish(false, true);
27430
+ }
27431
+ });
27432
+ input.addEventListener("blur", () => finish(true, true));
27433
+ surface.appendChild(input);
27434
+ input.focus();
27435
+ if (replacement === void 0) input.select();
27436
+ else input.setSelectionRange(input.value.length, input.value.length);
27437
+ }
26546
27438
  editDrawingWordArt(src) {
26547
27439
  if (this.wordArtTextEditor) {
26548
27440
  this.wordArtTextEditor.input.focus();
@@ -27192,8 +28084,14 @@ var _DocxEditor = class _DocxEditor {
27192
28084
  offset: endText.text.length,
27193
28085
  bias: endText.text.length > 0 ? "end" : void 0
27194
28086
  };
28087
+ const splitWasList = (() => {
28088
+ const pPr = paragraph.children.find((child4) => localName(child4.name) === "pPr");
28089
+ const style = pPr?.children.find((child4) => localName(child4.name) === "pStyle");
28090
+ return !!pPr?.children.some((child4) => localName(child4.name) === "numPr") || !!resolveParagraphStyleChain(this.host.doc.styles, attr(style, "val"), false).pPr.numbering;
28091
+ })();
27195
28092
  const split = this.splitParagraphCore();
27196
28093
  if (!split) return null;
28094
+ if (splitWasList) clearListParagraphFormatting(this.host.doc, split.after);
27197
28095
  const hasPageBreak = (element) => localName(element.name) === "br" && attr(element, "type") === "page" || element.children.some(hasPageBreak);
27198
28096
  const insertBeforeTrailingBreak = hasPageBreak(split.after);
27199
28097
  const afterSources = insertBeforeTrailingBreak ? [] : [split.after];
@@ -27793,6 +28691,14 @@ var _DocxEditor = class _DocxEditor {
27793
28691
  this.splitParagraphNoHistory();
27794
28692
  }
27795
28693
  splitParagraphNoHistory() {
28694
+ if (this.caret) {
28695
+ const paragraph = paragraphOf(this.host.doc, this.caret.t);
28696
+ if (paragraph && textElements(paragraph).every((text) => text.text.length === 0) && clearListParagraphFormatting(this.host.doc, paragraph)) {
28697
+ this.commit();
28698
+ this.focusText();
28699
+ return;
28700
+ }
28701
+ }
27796
28702
  const split = this.splitParagraphCore();
27797
28703
  const reparsed = split ? this.host.doc.reparseDirectBodyParagraphSplit(split.before, split.after) : null;
27798
28704
  if (reparsed && split && this.caret) {
@@ -28672,6 +29578,84 @@ function adjustIndent(doc, targets, direction) {
28672
29578
  if (touched) doc.refresh();
28673
29579
  return touched;
28674
29580
  }
29581
+ var DIVIDER_STYLES = /* @__PURE__ */ new Set([
29582
+ "single",
29583
+ "double",
29584
+ "dotted",
29585
+ "dashed",
29586
+ "thinThickSmallGap"
29587
+ ]);
29588
+ function paragraphDividerAt(doc, target) {
29589
+ const paragraph = paragraphOf(doc, target);
29590
+ const pPr = paragraph?.children.find((child4) => localName(child4.name) === "pPr");
29591
+ const pBdr = pPr?.children.find((child4) => localName(child4.name) === "pBdr");
29592
+ const bottom = pBdr?.children.find((child4) => localName(child4.name) === "bottom");
29593
+ if (!bottom) return null;
29594
+ const value = bottom.attrs[Object.keys(bottom.attrs).find((key) => localName(key) === "val") ?? ""];
29595
+ if (value === "none" || value === "nil") return null;
29596
+ const style = DIVIDER_STYLES.has(value) ? value : "single";
29597
+ const colorValue2 = bottom.attrs[Object.keys(bottom.attrs).find((key) => localName(key) === "color") ?? ""];
29598
+ const sizeValue = bottom.attrs[Object.keys(bottom.attrs).find((key) => localName(key) === "sz") ?? ""];
29599
+ const spaceValue = bottom.attrs[Object.keys(bottom.attrs).find((key) => localName(key) === "space") ?? ""];
29600
+ return {
29601
+ style,
29602
+ color: colorValue2 && colorValue2 !== "auto" ? `#${colorValue2.toUpperCase()}` : "#000000",
29603
+ widthPt: (parseInt(sizeValue ?? "4", 10) || 4) / 8,
29604
+ spacePt: parseInt(spaceValue ?? "0", 10) || 0
29605
+ };
29606
+ }
29607
+ function setParagraphDivider(doc, targets, divider) {
29608
+ const paragraphs = paragraphsOf(doc, targets);
29609
+ if (paragraphs.size === 0) return false;
29610
+ for (const paragraph of paragraphs) {
29611
+ const w = prefixOf13(paragraph);
29612
+ const pPr = ensurePPr(paragraph);
29613
+ let pBdr = pPr.children.find((child4) => localName(child4.name) === "pBdr");
29614
+ if (!divider) {
29615
+ if (!pBdr) continue;
29616
+ pBdr.children = pBdr.children.filter((child4) => localName(child4.name) !== "bottom");
29617
+ if (pBdr.children.length === 0) pPr.children.splice(pPr.children.indexOf(pBdr), 1);
29618
+ continue;
29619
+ }
29620
+ if (!pBdr) {
29621
+ pBdr = { name: `${w}pBdr`, attrs: {}, children: [], text: "" };
29622
+ const later = /* @__PURE__ */ new Set([
29623
+ "shd",
29624
+ "tabs",
29625
+ "spacing",
29626
+ "ind",
29627
+ "contextualSpacing",
29628
+ "mirrorIndents",
29629
+ "suppressOverlap",
29630
+ "jc",
29631
+ "textDirection",
29632
+ "textAlignment",
29633
+ "textboxTightWrap",
29634
+ "outlineLvl",
29635
+ "divId",
29636
+ "cnfStyle",
29637
+ "rPr",
29638
+ "sectPr",
29639
+ "pPrChange"
29640
+ ]);
29641
+ const index = pPr.children.findIndex((child4) => later.has(localName(child4.name)));
29642
+ pPr.children.splice(index === -1 ? pPr.children.length : index, 0, pBdr);
29643
+ }
29644
+ let bottom = pBdr.children.find((child4) => localName(child4.name) === "bottom");
29645
+ if (!bottom) {
29646
+ bottom = { name: `${w}bottom`, attrs: {}, children: [], text: "" };
29647
+ pBdr.children.push(bottom);
29648
+ }
29649
+ bottom.attrs = {
29650
+ [`${w}val`]: divider.style,
29651
+ [`${w}sz`]: String(Math.max(1, Math.round(divider.widthPt * 8))),
29652
+ [`${w}space`]: String(Math.max(0, Math.round(divider.spacePt))),
29653
+ [`${w}color`]: divider.color.replace(/^#/, "").toUpperCase()
29654
+ };
29655
+ }
29656
+ doc.refresh();
29657
+ return true;
29658
+ }
28675
29659
  function firstText2(node) {
28676
29660
  if (localName(node.name) === "t") return node;
28677
29661
  for (const child4 of node.children) {
@@ -29410,7 +30394,7 @@ var GUIDE_GROUPS = [
29410
30394
  items: [
29411
30395
  {
29412
30396
  title: "Open or upload a document",
29413
- description: "Choose a sample from Try a template or use Upload .docx to edit a local Word file in the browser."
30397
+ description: "Choose File \u2192 Open .docx to edit a local Word file, or choose a starting document from the template menu."
29414
30398
  },
29415
30399
  {
29416
30400
  title: "Zoom, review, and author",
@@ -29472,7 +30456,7 @@ var GUIDE_GROUPS = [
29472
30456
  },
29473
30457
  {
29474
30458
  title: "Shapes and lines",
29475
- description: "Insert a line, rectangle, rounded rectangle, ellipse, arrow, or callout. Select it to move, resize, rotate, change wrapping and layering, or edit fill and outline."
30459
+ description: "Insert a horizontal or vertical line, rectangle, rounded rectangle, ellipse, arrow, or callout. Select it to move, resize, rotate, change wrapping and layering, or edit its color, thickness, and solid, dashed, or dotted outline."
29476
30460
  },
29477
30461
  {
29478
30462
  title: "Text boxes and WordArt",
@@ -29538,7 +30522,7 @@ var GUIDE_GROUPS = [
29538
30522
  },
29539
30523
  {
29540
30524
  title: "Page borders and line numbers",
29541
- description: "Apply a repeating page border and enable continuous, per-page, per-section, or interval-based line numbering."
30525
+ description: "Apply a repeating page border with a preset or custom color and line weight, and enable continuous, per-page, per-section, or interval-based line numbering."
29542
30526
  },
29543
30527
  {
29544
30528
  title: "Arrange selected objects",
@@ -29559,7 +30543,7 @@ var GUIDE_GROUPS = [
29559
30543
  },
29560
30544
  {
29561
30545
  title: "Exact object properties",
29562
- description: "Use Size to set the object's width \xD7 height, Position for exact page coordinates, Fill for a shape's interior color, and Outline for its border or line color and thickness."
30546
+ description: "Set a line's color, weight, and solid, dashed, or dotted style directly in Insert \u2192 Shapes. After insertion, select it and choose Line style to revise those settings; Size and Position provide exact geometry."
29563
30547
  }
29564
30548
  ]
29565
30549
  },
@@ -29573,7 +30557,7 @@ var GUIDE_GROUPS = [
29573
30557
  "Open Insert \u2192 Header, then insert a text box in the repeating header layer.",
29574
30558
  "Enter 1 through 28 as separate paragraphs and select all of those numbered paragraphs.",
29575
30559
  "Open Home \u2192 Line & paragraph spacing \u2192 Exactly 24 pt, then choose Remove space before and Remove space after.",
29576
- "Use Insert \u2192 Shape \u2192 Line for each vertical rule, rotate and position it in the same repeating header layer.",
30560
+ "Open Insert \u2192 Shapes, set the line color, weight, and style, then insert a Vertical line for each rule and set its exact Size and Position in the repeating header layer.",
29577
30561
  "Choose Close to return to body text."
29578
30562
  ]
29579
30563
  },
@@ -29588,11 +30572,11 @@ var GUIDE_GROUPS = [
29588
30572
  },
29589
30573
  {
29590
30574
  title: "Resume horizontal rule",
29591
- description: "Use a body line when the rule belongs to one location in the document.",
30575
+ description: "Use a native paragraph divider so the rule stays attached to the r\xE9sum\xE9 heading or contact line.",
29592
30576
  steps: [
29593
- "Place the caret near the intended rule and choose Insert \u2192 Shape \u2192 Line.",
29594
- "Drag to position and size it; use Position for exact placement.",
29595
- "Choose Inline, Wrap, In front, or Behind according to how nearby text should flow."
30577
+ "Place the caret in the paragraph that should carry the rule and choose Insert \u2192 Divider.",
30578
+ "Choose a single, double, dashed, dotted, or thin + thick style, then set its color, width, and gap.",
30579
+ "Choose Apply divider. Return to the same paragraph and reopen Divider whenever you want to edit or remove it."
29596
30580
  ]
29597
30581
  },
29598
30582
  {
@@ -29601,7 +30585,7 @@ var GUIDE_GROUPS = [
29601
30585
  },
29602
30586
  {
29603
30587
  title: "Border around every page",
29604
- description: "Choose Layout \u2192 Page border for a true page border. Use header shapes when the design needs custom partial rules or ornaments."
30588
+ description: "Choose Layout \u2192 Page border \u2192 Custom border, set the color and line weight, then apply it to the whole document or current section."
29605
30589
  }
29606
30590
  ]
29607
30591
  }
@@ -29905,7 +30889,7 @@ function detectFonts() {
29905
30889
  };
29906
30890
  ctx.font = "16px monospace";
29907
30891
  const base = ctx.measureText(probe).width;
29908
- const always = /* @__PURE__ */ new Set(["Calibri", "Cambria"]);
30892
+ const always = /* @__PURE__ */ new Set(["Arial", "Calibri", "Cambria", "Courier New", "Times New Roman"]);
29909
30893
  availableFonts = FONT_CANDIDATES.filter((f) => always.has(f) || Math.abs(widthIn(f) - base) > 0.5);
29910
30894
  } catch {
29911
30895
  availableFonts = FONT_CANDIDATES;
@@ -30651,6 +31635,7 @@ function LinkMenu({ api }) {
30651
31635
  "input",
30652
31636
  {
30653
31637
  ref: inputRef,
31638
+ type: "url",
30654
31639
  value: url,
30655
31640
  placeholder: "Paste or type a link",
30656
31641
  onChange: (e) => setUrl(e.target.value),
@@ -30975,12 +31960,16 @@ function EquationMenu({ api }) {
30975
31960
  setError("Place the caret in editable text and enter a valid equation.");
30976
31961
  }
30977
31962
  };
31963
+ const anchor = open ? rootRef.current?.getBoundingClientRect() : null;
31964
+ const viewportWidth = typeof window === "undefined" ? 356 : window.innerWidth;
31965
+ const popoverWidth = Math.min(340, viewportWidth - 16);
31966
+ const popoverLeft = Math.max(8, Math.min(anchor?.left ?? 8, viewportWidth - popoverWidth - 8));
30978
31967
  return /* @__PURE__ */ jsxs2("span", { ref: rootRef, style: { position: "relative", display: "inline-block" }, children: [
30979
31968
  /* @__PURE__ */ jsxs2("button", { title: "Insert equation", style: btnStyle(open), onMouseDown: (event) => event.preventDefault(), onClick: () => setOpen(!open), children: [
30980
31969
  /* @__PURE__ */ jsx2("span", { style: { fontFamily: "'Cambria Math', serif", fontSize: 18 }, children: "\u03C0" }),
30981
31970
  /* @__PURE__ */ jsx2("span", { style: { marginLeft: 5 }, children: "Equation" })
30982
31971
  ] }),
30983
- open && /* @__PURE__ */ jsxs2("div", { style: { position: "absolute", top: 28, right: 0, zIndex: 100, width: 340, padding: 10, background: T.popoverBg, border: `1px solid ${T.border}`, borderRadius: 8, boxShadow: T.popoverShadow }, children: [
31972
+ open && /* @__PURE__ */ jsxs2("div", { "data-dxw-equation-menu": "", style: { position: "fixed", top: anchor?.bottom ?? 28, left: popoverLeft, zIndex: 100, width: popoverWidth, boxSizing: "border-box", padding: 10, background: T.popoverBg, border: `1px solid ${T.border}`, borderRadius: 8, boxShadow: T.popoverShadow }, children: [
30984
31973
  /* @__PURE__ */ jsx2("div", { style: { font: "600 12px system-ui, sans-serif", marginBottom: 5, color: T.fg }, children: "Linear equation" }),
30985
31974
  /* @__PURE__ */ jsx2(
30986
31975
  "input",
@@ -31055,15 +32044,88 @@ function SymbolMenu({ api }) {
31055
32044
  }
31056
32045
  var SHAPES = [
31057
32046
  ["line", "Line", "\u2015"],
32047
+ ["verticalLine", "Vertical line", "\u2502"],
31058
32048
  ["rectangle", "Rectangle", "\u25AD"],
31059
32049
  ["roundedRectangle", "Rounded rectangle", "\u25A2"],
31060
32050
  ["ellipse", "Ellipse", "\u25EF"],
31061
32051
  ["diamond", "Diamond", "\u25C7"],
31062
32052
  ["textBox", "Text box", "T"]
31063
32053
  ];
32054
+ function DividerMenu({ api }) {
32055
+ const [open, setOpen] = useState2(false);
32056
+ const [style, setStyle] = useState2("single");
32057
+ const [color, setColor] = useState2("#000000");
32058
+ const [widthPt, setWidthPt] = useState2(1);
32059
+ const [spacePt, setSpacePt] = useState2(1);
32060
+ const rootRef = useRef2(null);
32061
+ useEffect2(() => {
32062
+ if (!open) return;
32063
+ const close = (event) => {
32064
+ if (!rootRef.current?.contains(event.target)) setOpen(false);
32065
+ };
32066
+ document.addEventListener("mousedown", close);
32067
+ return () => document.removeEventListener("mousedown", close);
32068
+ }, [open]);
32069
+ const toggle = () => {
32070
+ if (!open) {
32071
+ const current = api?.getParagraphDivider();
32072
+ if (current) {
32073
+ setStyle(current.style);
32074
+ setColor(current.color);
32075
+ setWidthPt(current.widthPt);
32076
+ setSpacePt(current.spacePt);
32077
+ }
32078
+ }
32079
+ setOpen(!open);
32080
+ };
32081
+ const apply = () => {
32082
+ if (api?.setParagraphDivider({ style, color, widthPt, spacePt })) setOpen(false);
32083
+ };
32084
+ const previewStyle = style === "double" || style === "thinThickSmallGap" ? "double" : style;
32085
+ return /* @__PURE__ */ jsxs2("span", { ref: rootRef, style: { position: "relative", display: "inline-block" }, children: [
32086
+ /* @__PURE__ */ jsx2("button", { title: "Insert or edit divider", style: btnStyle(open), onMouseDown: (event) => event.preventDefault(), onClick: toggle, children: "Divider" }),
32087
+ open && /* @__PURE__ */ jsxs2("div", { style: { position: "absolute", top: 28, right: 0, zIndex: 100, width: 270, padding: 10, display: "grid", gap: 8, background: T.popoverBg, border: `1px solid ${T.border}`, borderRadius: 8, boxShadow: T.popoverShadow }, children: [
32088
+ /* @__PURE__ */ jsx2("div", { style: { color: T.muted, font: "11.5px system-ui, sans-serif" }, children: "Horizontal rule below the current paragraph" }),
32089
+ /* @__PURE__ */ jsx2("div", { "aria-hidden": "true", style: { height: 12, borderBottom: `${Math.max(widthPt, 1)}px ${previewStyle} ${color}` } }),
32090
+ /* @__PURE__ */ jsxs2("label", { style: { display: "grid", gap: 3, color: T.muted, font: "11.5px system-ui, sans-serif" }, children: [
32091
+ "Style",
32092
+ /* @__PURE__ */ jsxs2("select", { "aria-label": "Divider style", value: style, onChange: (event) => setStyle(event.target.value), style: { border: `1px solid ${T.border}`, borderRadius: 5, padding: "5px 7px", background: T.popoverBg, color: T.fg }, children: [
32093
+ /* @__PURE__ */ jsx2("option", { value: "single", children: "Single" }),
32094
+ /* @__PURE__ */ jsx2("option", { value: "double", children: "Double" }),
32095
+ /* @__PURE__ */ jsx2("option", { value: "dashed", children: "Dashed" }),
32096
+ /* @__PURE__ */ jsx2("option", { value: "dotted", children: "Dotted" }),
32097
+ /* @__PURE__ */ jsx2("option", { value: "thinThickSmallGap", children: "Thin + thick" })
32098
+ ] })
32099
+ ] }),
32100
+ /* @__PURE__ */ jsxs2("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 7 }, children: [
32101
+ /* @__PURE__ */ jsxs2("label", { style: { display: "grid", gap: 3, color: T.muted, font: "11.5px system-ui, sans-serif" }, children: [
32102
+ "Color",
32103
+ /* @__PURE__ */ jsx2("input", { "aria-label": "Divider color", type: "color", value: color, onChange: (event) => setColor(event.target.value), style: { width: "100%", height: 30, border: `1px solid ${T.border}`, borderRadius: 5, background: T.popoverBg } })
32104
+ ] }),
32105
+ /* @__PURE__ */ jsxs2("label", { style: { display: "grid", gap: 3, color: T.muted, font: "11.5px system-ui, sans-serif" }, children: [
32106
+ "Width (pt)",
32107
+ /* @__PURE__ */ jsx2("input", { "aria-label": "Divider width in points", type: "number", min: "0.25", step: "0.25", value: widthPt, onChange: (event) => setWidthPt(Number(event.target.value)), style: { width: "100%", boxSizing: "border-box", border: `1px solid ${T.border}`, borderRadius: 5, padding: "5px 6px", color: T.fg, background: T.popoverBg } })
32108
+ ] }),
32109
+ /* @__PURE__ */ jsxs2("label", { style: { display: "grid", gap: 3, color: T.muted, font: "11.5px system-ui, sans-serif" }, children: [
32110
+ "Gap (pt)",
32111
+ /* @__PURE__ */ jsx2("input", { "aria-label": "Divider gap in points", type: "number", min: "0", step: "1", value: spacePt, onChange: (event) => setSpacePt(Number(event.target.value)), style: { width: "100%", boxSizing: "border-box", border: `1px solid ${T.border}`, borderRadius: 5, padding: "5px 6px", color: T.fg, background: T.popoverBg } })
32112
+ ] })
32113
+ ] }),
32114
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "space-between", gap: 8 }, children: [
32115
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => {
32116
+ if (api?.setParagraphDivider(null)) setOpen(false);
32117
+ }, style: { ...pillBtn, background: T.popoverBg, color: T.fg, border: `1px solid ${T.border}` }, children: "Remove" }),
32118
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: apply, style: pillBtn, children: "Apply divider" })
32119
+ ] })
32120
+ ] })
32121
+ ] });
32122
+ }
31064
32123
  function ShapeMenu({ api }) {
31065
32124
  const [open, setOpen] = useState2(false);
31066
32125
  const [text, setText] = useState2("");
32126
+ const [lineColor, setLineColor] = useState2("#404040");
32127
+ const [lineWidth, setLineWidth] = useState2("1.33");
32128
+ const [lineDash, setLineDash] = useState2("solid");
31067
32129
  const rootRef = useRef2(null);
31068
32130
  useEffect2(() => {
31069
32131
  if (!open) return;
@@ -31074,14 +32136,17 @@ function ShapeMenu({ api }) {
31074
32136
  return () => document.removeEventListener("mousedown", close);
31075
32137
  }, [open]);
31076
32138
  const insert = (preset) => {
31077
- if (api?.insertShape(preset, text)) {
32139
+ const isLine = preset === "line" || preset === "verticalLine";
32140
+ const width = Number(lineWidth);
32141
+ if (isLine && (!Number.isFinite(width) || width <= 0)) return;
32142
+ if (api?.insertShape(preset, text, isLine ? { color: lineColor, width, dash: lineDash } : void 0)) {
31078
32143
  setText("");
31079
32144
  setOpen(false);
31080
32145
  }
31081
32146
  };
31082
32147
  return /* @__PURE__ */ jsxs2("span", { ref: rootRef, style: { position: "relative", display: "inline-block" }, children: [
31083
32148
  /* @__PURE__ */ jsx2("button", { title: "Insert shape", style: btnStyle(open), onMouseDown: (event) => event.preventDefault(), onClick: () => setOpen(!open), children: /* @__PURE__ */ jsx2("span", { style: { fontSize: 17 }, children: "\u25C7" }) }),
31084
- open && /* @__PURE__ */ jsxs2("div", { style: { position: "absolute", top: 28, right: 0, zIndex: 100, width: 250, padding: 10, background: T.popoverBg, border: `1px solid ${T.border}`, borderRadius: 8, boxShadow: T.popoverShadow }, children: [
32149
+ open && /* @__PURE__ */ jsxs2("div", { style: { position: "absolute", top: 28, right: 0, zIndex: 100, width: 290, padding: 10, background: T.popoverBg, border: `1px solid ${T.border}`, borderRadius: 8, boxShadow: T.popoverShadow }, children: [
31085
32150
  /* @__PURE__ */ jsx2(
31086
32151
  "input",
31087
32152
  {
@@ -31092,6 +32157,53 @@ function ShapeMenu({ api }) {
31092
32157
  style: { width: "100%", boxSizing: "border-box", border: `1px solid ${T.border}`, borderRadius: 6, padding: "5px 8px", font: "13px system-ui, sans-serif", outline: "none" }
31093
32158
  }
31094
32159
  ),
32160
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: 9, color: T.muted, font: "11.5px system-ui, sans-serif" }, children: "Line appearance" }),
32161
+ /* @__PURE__ */ jsxs2("div", { style: { display: "grid", gridTemplateColumns: "auto 82px 88px", gap: 6, alignItems: "end", marginTop: 5 }, children: [
32162
+ /* @__PURE__ */ jsx2(
32163
+ ColorMenu,
32164
+ {
32165
+ current: lineColor,
32166
+ title: "Line color",
32167
+ trigger: /* @__PURE__ */ jsxs2(Fragment2, { children: [
32168
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 12 }, children: "Color" }),
32169
+ /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", style: { width: 16, height: 16, borderRadius: 3, border: `1px solid ${T.border}`, background: lineColor } })
32170
+ ] }),
32171
+ onPick: setLineColor
32172
+ }
32173
+ ),
32174
+ /* @__PURE__ */ jsxs2("label", { style: { display: "grid", gap: 3, color: T.muted, font: "10.5px system-ui, sans-serif" }, children: [
32175
+ "Weight (px)",
32176
+ /* @__PURE__ */ jsx2(
32177
+ "input",
32178
+ {
32179
+ "aria-label": "Line width in pixels",
32180
+ type: "number",
32181
+ min: "0.25",
32182
+ step: "0.25",
32183
+ value: lineWidth,
32184
+ onChange: (event) => setLineWidth(event.target.value),
32185
+ style: { width: "100%", height: 28, boxSizing: "border-box", border: `1px solid ${T.border}`, borderRadius: 5, padding: "3px 5px", color: T.fg, background: T.popoverBg }
32186
+ }
32187
+ )
32188
+ ] }),
32189
+ /* @__PURE__ */ jsxs2("label", { style: { display: "grid", gap: 3, color: T.muted, font: "10.5px system-ui, sans-serif" }, children: [
32190
+ "Style",
32191
+ /* @__PURE__ */ jsxs2(
32192
+ "select",
32193
+ {
32194
+ "aria-label": "Line style",
32195
+ value: lineDash,
32196
+ onChange: (event) => setLineDash(event.target.value),
32197
+ style: { width: "100%", height: 28, border: `1px solid ${T.border}`, borderRadius: 5, padding: "3px 4px", color: T.fg, background: T.popoverBg },
32198
+ children: [
32199
+ /* @__PURE__ */ jsx2("option", { value: "solid", children: "Solid" }),
32200
+ /* @__PURE__ */ jsx2("option", { value: "dashed", children: "Dashed" }),
32201
+ /* @__PURE__ */ jsx2("option", { value: "dotted", children: "Dotted" })
32202
+ ]
32203
+ }
32204
+ )
32205
+ ] })
32206
+ ] }),
31095
32207
  /* @__PURE__ */ jsx2("div", { style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 6, marginTop: 8 }, children: SHAPES.map(([preset, label, glyph]) => /* @__PURE__ */ jsxs2("button", { title: `Insert ${label}`, onClick: () => insert(preset), style: { minHeight: 48, border: `1px solid ${T.border}`, borderRadius: 6, background: T.popoverBg, color: T.fg, cursor: "pointer", font: "12px system-ui, sans-serif" }, children: [
31096
32208
  /* @__PURE__ */ jsx2("span", { style: { display: "block", fontSize: 20, lineHeight: 1 }, children: glyph }),
31097
32209
  /* @__PURE__ */ jsx2("span", { children: label })
@@ -31187,12 +32299,12 @@ function WordArtMenu({ api }) {
31187
32299
  ] })
31188
32300
  ] });
31189
32301
  }
31190
- function ChartMenu({ api }) {
32302
+ function ChartMenu({ api, label = "Chart" }) {
31191
32303
  const [open, setOpen] = useState2(false);
31192
32304
  const [type, setType] = useState2("column");
31193
- const [title, setTitle] = useState2("Quarterly sales");
31194
- const [categories, setCategories] = useState2("Q1, Q2, Q3, Q4");
31195
- const [series, setSeries] = useState2("Revenue: 12, 19, 15, 24\nCosts: 8, 11, 10, 14");
32305
+ const [title, setTitle] = useState2("");
32306
+ const [categories, setCategories] = useState2(["", ""]);
32307
+ const [series, setSeries] = useState2([{ name: "", values: ["", ""] }]);
31196
32308
  const [error, setError] = useState2("");
31197
32309
  const rootRef = useRef2(null);
31198
32310
  useEffect2(() => {
@@ -31204,28 +32316,52 @@ function ChartMenu({ api }) {
31204
32316
  return () => document.removeEventListener("mousedown", close);
31205
32317
  }, [open]);
31206
32318
  const submit = () => {
31207
- const categoryValues = categories.split(",").map((value) => value.trim()).filter(Boolean);
31208
- const seriesValues = series.split(/\r?\n/).map((line2) => {
31209
- const separator = line2.indexOf(":");
31210
- if (separator === -1) return null;
31211
- const name = line2.slice(0, separator).trim();
31212
- const values = line2.slice(separator + 1).split(",").map((value) => Number(value.trim()));
31213
- return name && values.length === categoryValues.length && values.every(Number.isFinite) ? { name, values } : null;
31214
- }).filter((value) => value !== null);
31215
- if (!categoryValues.length || !seriesValues.length || seriesValues.length !== series.split(/\r?\n/).filter((line2) => line2.trim()).length) {
31216
- setError("Use comma-separated categories and one \u201CName: 1, 2, 3\u201D series per line.");
32319
+ const categoryValues = categories.map((value) => value.trim());
32320
+ const rawSeries = series.map((entry) => ({
32321
+ name: entry.name.trim(),
32322
+ values: entry.values.map((value) => value.trim())
32323
+ }));
32324
+ if (categoryValues.some((value) => !value) || rawSeries.some((entry) => !entry.name || entry.values.some((value) => value === "" || !Number.isFinite(Number(value))))) {
32325
+ setError("Give every category and series a name, and enter a number in every data cell.");
31217
32326
  return;
31218
32327
  }
32328
+ const seriesValues = rawSeries.map((entry) => ({ name: entry.name, values: entry.values.map(Number) }));
31219
32329
  const data = { type, title, categories: categoryValues, series: seriesValues };
31220
32330
  if (api?.updateSelectedChart(data) || api?.insertChart(data)) {
31221
32331
  setError("");
31222
32332
  setOpen(false);
31223
32333
  }
31224
32334
  };
32335
+ const addCategory = () => {
32336
+ setCategories([...categories, ""]);
32337
+ setSeries(series.map((entry) => ({ ...entry, values: [...entry.values, ""] })));
32338
+ };
32339
+ const removeCategory = (index) => {
32340
+ if (categories.length === 1) return;
32341
+ setCategories(categories.filter((_, itemIndex) => itemIndex !== index));
32342
+ setSeries(series.map((entry) => ({ ...entry, values: entry.values.filter((_, itemIndex) => itemIndex !== index) })));
32343
+ };
32344
+ const anchor = open ? rootRef.current?.getBoundingClientRect() : null;
32345
+ const viewportWidth = typeof window === "undefined" ? 456 : window.innerWidth;
32346
+ const popoverWidth = Math.min(440, viewportWidth - 16);
32347
+ const popoverLeft = Math.max(8, Math.min(anchor?.left ?? 8, viewportWidth - popoverWidth - 8));
31225
32348
  const fieldStyle = { width: "100%", boxSizing: "border-box", border: `1px solid ${T.border}`, borderRadius: 6, padding: "6px 8px", font: "13px system-ui, sans-serif", color: T.fg, background: T.popoverBg };
32349
+ const toggle = () => {
32350
+ if (open) {
32351
+ setOpen(false);
32352
+ return;
32353
+ }
32354
+ const selected = api?.getSelectedChart();
32355
+ setType(selected?.type ?? "column");
32356
+ setTitle(selected?.title ?? "");
32357
+ setCategories(selected ? [...selected.categories] : ["", ""]);
32358
+ setSeries(selected ? selected.series.map((entry) => ({ name: entry.name, values: entry.values.map(String) })) : [{ name: "", values: ["", ""] }]);
32359
+ setError("");
32360
+ setOpen(true);
32361
+ };
31226
32362
  return /* @__PURE__ */ jsxs2("span", { ref: rootRef, style: { position: "relative", display: "inline-block" }, children: [
31227
- /* @__PURE__ */ jsx2("button", { title: "Insert or edit chart", style: btnStyle(open), onMouseDown: (event) => event.preventDefault(), onClick: () => setOpen(!open), children: "Chart" }),
31228
- open && /* @__PURE__ */ jsxs2("div", { style: { position: "absolute", top: 28, right: 0, zIndex: 100, width: 320, padding: 10, display: "grid", gap: 7, background: T.popoverBg, border: `1px solid ${T.border}`, borderRadius: 8, boxShadow: T.popoverShadow }, children: [
32363
+ /* @__PURE__ */ jsx2("button", { title: "Insert or edit chart", style: btnStyle(open), onMouseDown: (event) => event.preventDefault(), onClick: toggle, children: label }),
32364
+ open && /* @__PURE__ */ jsxs2("div", { style: { position: "fixed", top: anchor?.bottom ?? 28, left: popoverLeft, zIndex: 100, width: popoverWidth, maxHeight: "calc(100vh - 48px)", overflow: "auto", boxSizing: "border-box", padding: 10, display: "grid", gap: 8, background: T.popoverBg, border: `1px solid ${T.border}`, borderRadius: 8, boxShadow: T.popoverShadow }, children: [
31229
32365
  /* @__PURE__ */ jsx2(
31230
32366
  ToolbarMenuSelect,
31231
32367
  {
@@ -31244,58 +32380,145 @@ function ChartMenu({ api }) {
31244
32380
  }
31245
32381
  ),
31246
32382
  /* @__PURE__ */ jsx2("input", { "aria-label": "Chart title", value: title, onChange: (event) => setTitle(event.target.value), placeholder: "Chart title", style: fieldStyle }),
31247
- /* @__PURE__ */ jsx2("input", { "aria-label": "Chart categories", value: categories, onChange: (event) => setCategories(event.target.value), placeholder: "Q1, Q2, Q3, Q4", style: fieldStyle }),
31248
- /* @__PURE__ */ jsx2("textarea", { "aria-label": "Chart series", rows: 3, value: series, onChange: (event) => setSeries(event.target.value), style: { ...fieldStyle, resize: "vertical" } }),
31249
- /* @__PURE__ */ jsx2("div", { style: { color: T.muted, font: "11.5px system-ui, sans-serif" }, children: "Each series: Name: value, value, value" }),
32383
+ /* @__PURE__ */ jsxs2("div", { role: "group", "aria-label": "Chart data", style: { display: "grid", gap: 7 }, children: [
32384
+ /* @__PURE__ */ jsx2("strong", { style: { color: T.fg, font: "600 11.5px system-ui, sans-serif" }, children: "Data" }),
32385
+ /* @__PURE__ */ jsx2("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs2("div", { style: { display: "grid", gridTemplateColumns: `minmax(110px,1.2fr) repeat(${categories.length},minmax(72px,1fr)) 62px`, gap: 5, minWidth: categories.length > 3 ? 430 : void 0 }, children: [
32386
+ /* @__PURE__ */ jsx2("span", { style: { alignSelf: "center", color: T.muted, font: "11px system-ui, sans-serif" }, children: "Series" }),
32387
+ categories.map((category, index) => /* @__PURE__ */ jsxs2("label", { style: { display: "grid", gridTemplateColumns: "1fr auto", gap: 3 }, children: [
32388
+ /* @__PURE__ */ jsx2(
32389
+ "input",
32390
+ {
32391
+ "aria-label": `Chart category ${index + 1}`,
32392
+ value: category,
32393
+ placeholder: "Category",
32394
+ onChange: (event) => setCategories(categories.map((value, itemIndex) => itemIndex === index ? event.target.value : value)),
32395
+ style: fieldStyle
32396
+ }
32397
+ ),
32398
+ categories.length > 1 && /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Remove chart category ${index + 1}`, onClick: () => removeCategory(index), style: { ...pillBtn, padding: "0 6px" }, children: "\xD7" })
32399
+ ] }, index)),
32400
+ /* @__PURE__ */ jsx2("span", {}),
32401
+ series.map((entry, seriesIndex) => /* @__PURE__ */ jsxs2(Fragment, { children: [
32402
+ /* @__PURE__ */ jsx2(
32403
+ "input",
32404
+ {
32405
+ "aria-label": `Chart series ${seriesIndex + 1} name`,
32406
+ value: entry.name,
32407
+ placeholder: "Series",
32408
+ onChange: (event) => setSeries(series.map((value, itemIndex) => itemIndex === seriesIndex ? { ...value, name: event.target.value } : value)),
32409
+ style: fieldStyle
32410
+ }
32411
+ ),
32412
+ entry.values.map((value, valueIndex) => /* @__PURE__ */ jsx2(
32413
+ "input",
32414
+ {
32415
+ "aria-label": `Chart series ${seriesIndex + 1} value ${valueIndex + 1}`,
32416
+ type: "number",
32417
+ step: "any",
32418
+ value,
32419
+ placeholder: "0",
32420
+ onChange: (event) => setSeries(series.map((seriesValue, itemIndex) => itemIndex === seriesIndex ? { ...seriesValue, values: seriesValue.values.map((itemValue, itemValueIndex) => itemValueIndex === valueIndex ? event.target.value : itemValue) } : seriesValue)),
32421
+ style: fieldStyle
32422
+ },
32423
+ valueIndex
32424
+ )),
32425
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Remove chart series ${seriesIndex + 1}`, disabled: series.length === 1, onClick: () => setSeries(series.filter((_, itemIndex) => itemIndex !== seriesIndex)), style: { ...pillBtn, padding: "0 7px" }, children: "Remove" })
32426
+ ] }, seriesIndex))
32427
+ ] }) }),
32428
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 6 }, children: [
32429
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => setSeries([...series, { name: "", values: categories.map(() => "") }]), style: { ...pillBtn, background: T.popoverBg, color: T.fg }, children: "Add series" }),
32430
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: addCategory, style: { ...pillBtn, background: T.popoverBg, color: T.fg }, children: "Add category" })
32431
+ ] })
32432
+ ] }),
31250
32433
  error && /* @__PURE__ */ jsx2("div", { role: "alert", style: { color: "#c5221f", font: "11.5px system-ui, sans-serif" }, children: error }),
31251
32434
  /* @__PURE__ */ jsx2("button", { onClick: submit, style: { border: 0, borderRadius: 6, padding: "7px 10px", background: T.accent, color: T.accentFg, cursor: "pointer", font: "600 12px system-ui, sans-serif" }, children: "Insert or update chart" })
31252
32435
  ] })
31253
32436
  ] });
31254
32437
  }
31255
- function SmartArtMenu({ api }) {
32438
+ function SmartArtMenu({ api, label = "SmartArt" }) {
31256
32439
  const [open, setOpen] = useState2(false);
32440
+ const [step, setStep] = useState2("layout");
32441
+ const [editing, setEditing] = useState2(false);
31257
32442
  const [layout, setLayout] = useState2("process");
31258
- const [items, setItems] = useState2("Discover\nDesign\nDeliver");
32443
+ const [items, setItems] = useState2([""]);
31259
32444
  const rootRef = useRef2(null);
31260
- useEffect2(() => {
31261
- if (!open) return;
31262
- const close = (event) => {
31263
- if (!rootRef.current?.contains(event.target)) setOpen(false);
31264
- };
31265
- document.addEventListener("mousedown", close);
31266
- return () => document.removeEventListener("mousedown", close);
31267
- }, [open]);
31268
32445
  const submit = () => {
31269
- const values = items.split(/\r?\n/).map((value) => value.trim()).filter(Boolean);
32446
+ const values = items.map((value) => value.trim()).filter(Boolean);
31270
32447
  if (!values.length) return;
31271
32448
  const data = { layout, items: values };
31272
32449
  if (api?.updateSelectedSmartArt(data) || api?.insertSmartArt(data)) setOpen(false);
31273
32450
  };
31274
32451
  const fieldStyle = { width: "100%", boxSizing: "border-box", border: `1px solid ${T.border}`, borderRadius: 6, padding: "6px 8px", font: "13px system-ui, sans-serif", color: T.fg, background: T.popoverBg };
32452
+ const toggle = () => {
32453
+ if (open) {
32454
+ setOpen(false);
32455
+ return;
32456
+ }
32457
+ const selected = api?.getSelectedSmartArt();
32458
+ setEditing(!!selected);
32459
+ setLayout(selected?.layout ?? "process");
32460
+ setItems(selected ? [...selected.items] : [""]);
32461
+ setStep(selected ? "items" : "layout");
32462
+ setOpen(true);
32463
+ };
32464
+ const layouts = [
32465
+ { value: "process", label: "Process" },
32466
+ { value: "cycle", label: "Cycle" },
32467
+ { value: "hierarchy", label: "Hierarchy" },
32468
+ { value: "list", label: "List" }
32469
+ ];
32470
+ const preview = (value) => {
32471
+ const node = { width: 22, height: 13, borderRadius: 3, background: T.accent };
32472
+ if (value === "list") return /* @__PURE__ */ jsx2("div", { style: { display: "grid", gap: 4 }, children: [0, 1, 2].map((key) => /* @__PURE__ */ jsx2("span", { style: { ...node, width: 72 } }, key)) });
32473
+ if (value === "hierarchy") return /* @__PURE__ */ jsxs2("div", { style: { display: "grid", justifyItems: "center", gap: 8 }, children: [
32474
+ /* @__PURE__ */ jsx2("span", { style: node }),
32475
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 12 }, children: [
32476
+ /* @__PURE__ */ jsx2("span", { style: node }),
32477
+ /* @__PURE__ */ jsx2("span", { style: node })
32478
+ ] })
32479
+ ] });
32480
+ if (value === "cycle") return /* @__PURE__ */ jsx2("div", { style: { position: "relative", width: 76, height: 48 }, children: [[27, 0], [53, 18], [27, 35], [1, 18]].map(([left, top], key) => /* @__PURE__ */ jsx2("span", { style: { ...node, position: "absolute", left, top } }, key)) });
32481
+ return /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 5 }, children: [
32482
+ /* @__PURE__ */ jsx2("span", { style: node }),
32483
+ /* @__PURE__ */ jsx2("span", { children: "\u2192" }),
32484
+ /* @__PURE__ */ jsx2("span", { style: node }),
32485
+ /* @__PURE__ */ jsx2("span", { children: "\u2192" }),
32486
+ /* @__PURE__ */ jsx2("span", { style: node })
32487
+ ] });
32488
+ };
31275
32489
  return /* @__PURE__ */ jsxs2("span", { ref: rootRef, style: { position: "relative", display: "inline-block" }, children: [
31276
- /* @__PURE__ */ jsx2("button", { title: "Insert or edit SmartArt", style: btnStyle(open), onMouseDown: (event) => event.preventDefault(), onClick: () => setOpen(!open), children: "SmartArt" }),
31277
- open && /* @__PURE__ */ jsxs2("div", { style: { position: "absolute", top: 28, right: 0, zIndex: 100, width: 280, padding: 10, display: "grid", gap: 7, background: T.popoverBg, border: `1px solid ${T.border}`, borderRadius: 8, boxShadow: T.popoverShadow }, children: [
31278
- /* @__PURE__ */ jsx2(
31279
- ToolbarMenuSelect,
31280
- {
31281
- value: layout,
31282
- ariaLabel: "SmartArt layout",
31283
- width: "100%",
31284
- menuWidth: 260,
31285
- options: [
31286
- { value: "process", label: "Process" },
31287
- { value: "cycle", label: "Cycle" },
31288
- { value: "hierarchy", label: "Hierarchy" },
31289
- { value: "list", label: "List" }
31290
- ],
31291
- onChange: (value) => setLayout(value),
31292
- style: fieldStyle
31293
- }
31294
- ),
31295
- /* @__PURE__ */ jsx2("textarea", { "aria-label": "SmartArt items", rows: 5, value: items, onChange: (event) => setItems(event.target.value), style: { ...fieldStyle, resize: "vertical" } }),
31296
- /* @__PURE__ */ jsx2("div", { style: { color: T.muted, font: "11.5px system-ui, sans-serif" }, children: "One diagram item per line." }),
31297
- /* @__PURE__ */ jsx2("button", { disabled: !items.trim(), onClick: submit, style: { border: 0, borderRadius: 6, padding: "7px 10px", background: items.trim() ? T.accent : T.border, color: T.accentFg, cursor: items.trim() ? "pointer" : "default", font: "600 12px system-ui, sans-serif" }, children: "Insert or update SmartArt" })
31298
- ] })
32490
+ /* @__PURE__ */ jsx2("button", { title: "Insert or edit SmartArt", style: btnStyle(open), onMouseDown: (event) => event.preventDefault(), onClick: toggle, children: label }),
32491
+ open && createPortal2(
32492
+ /* @__PURE__ */ jsx2("div", { style: { position: "fixed", inset: 0, zIndex: 1e3, display: "grid", placeItems: "center", padding: 16, background: "rgba(0,0,0,.34)" }, onMouseDown: (event) => event.target === event.currentTarget && setOpen(false), children: /* @__PURE__ */ jsxs2("div", { role: "dialog", "aria-modal": "true", "aria-label": editing ? "Edit SmartArt" : "Insert SmartArt", style: { width: "min(560px,calc(100vw - 32px))", maxHeight: "calc(100vh - 32px)", overflow: "auto", boxSizing: "border-box", padding: 18, display: "grid", gap: 14, color: T.fg, background: T.popoverBg, border: `1px solid ${T.border}`, borderRadius: 12, boxShadow: T.popoverShadow }, children: [
32493
+ /* @__PURE__ */ jsxs2("div", { children: [
32494
+ /* @__PURE__ */ jsx2("strong", { style: { display: "block", font: "600 18px system-ui, sans-serif" }, children: step === "layout" ? "Choose a SmartArt layout" : editing ? "Edit SmartArt" : "Add SmartArt text" }),
32495
+ /* @__PURE__ */ jsx2("span", { style: { color: T.muted, font: "12px system-ui, sans-serif" }, children: step === "layout" ? "Choose one of the supported layout families." : layouts.find((item) => item.value === layout)?.label })
32496
+ ] }),
32497
+ step === "layout" ? /* @__PURE__ */ jsx2("div", { role: "group", "aria-label": "SmartArt layouts", style: { display: "grid", gridTemplateColumns: "repeat(2,minmax(0,1fr))", gap: 10 }, children: layouts.map((option) => /* @__PURE__ */ jsxs2("button", { type: "button", "aria-label": `${option.label} SmartArt`, onClick: () => {
32498
+ setLayout(option.value);
32499
+ setStep("items");
32500
+ }, style: { minHeight: 118, display: "grid", placeItems: "center", gap: 9, padding: 12, border: `1px solid ${layout === option.value ? T.accent : T.border}`, borderRadius: 9, background: layout === option.value ? T.activeBg : T.popoverBg, color: T.fg, cursor: "pointer" }, children: [
32501
+ preview(option.value),
32502
+ /* @__PURE__ */ jsx2("span", { style: { font: "600 13px system-ui, sans-serif" }, children: option.label })
32503
+ ] }, option.value)) }) : /* @__PURE__ */ jsxs2("div", { role: "group", "aria-label": "SmartArt items", style: { display: "grid", gap: 7 }, children: [
32504
+ items.map((item, index) => /* @__PURE__ */ jsxs2("div", { style: { display: "grid", gridTemplateColumns: "1fr auto auto auto", gap: 5 }, children: [
32505
+ /* @__PURE__ */ jsx2("input", { "aria-label": `SmartArt item ${index + 1}`, value: item, placeholder: "Item", onChange: (event) => setItems(items.map((value, itemIndex) => itemIndex === index ? event.target.value : value)), style: fieldStyle }),
32506
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Move SmartArt item ${index + 1} up`, disabled: index === 0, onClick: () => setItems(items.map((value, itemIndex) => itemIndex === index - 1 ? items[index] : itemIndex === index ? items[index - 1] : value)), style: { ...pillBtn, padding: "0 8px" }, children: "\u2191" }),
32507
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Move SmartArt item ${index + 1} down`, disabled: index === items.length - 1, onClick: () => setItems(items.map((value, itemIndex) => itemIndex === index ? items[index + 1] : itemIndex === index + 1 ? items[index] : value)), style: { ...pillBtn, padding: "0 8px" }, children: "\u2193" }),
32508
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Remove SmartArt item ${index + 1}`, disabled: items.length === 1, onClick: () => setItems(items.filter((_, itemIndex) => itemIndex !== index)), style: { ...pillBtn, padding: "0 8px" }, children: "\xD7" })
32509
+ ] }, index)),
32510
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => setItems([...items, ""]), style: { ...pillBtn, justifySelf: "start", background: T.popoverBg, color: T.fg }, children: "Add item" })
32511
+ ] }),
32512
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "space-between", gap: 8 }, children: [
32513
+ /* @__PURE__ */ jsx2("div", { children: step === "items" && /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => setStep("layout"), style: { ...pillBtn, background: T.popoverBg, color: T.fg }, children: "Back" }) }),
32514
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 7 }, children: [
32515
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => setOpen(false), style: { ...pillBtn, background: T.popoverBg, color: T.fg }, children: "Cancel" }),
32516
+ step === "items" && /* @__PURE__ */ jsx2("button", { type: "button", disabled: !items.some((item) => item.trim()), onClick: submit, style: { border: 0, borderRadius: 6, padding: "7px 12px", background: items.some((item) => item.trim()) ? T.accent : T.border, color: T.accentFg, cursor: items.some((item) => item.trim()) ? "pointer" : "default", font: "600 12px system-ui, sans-serif" }, children: editing ? "Update" : "Insert" })
32517
+ ] })
32518
+ ] })
32519
+ ] }) }),
32520
+ document.body
32521
+ )
31299
32522
  ] });
31300
32523
  }
31301
32524
  function MediaMenu({ api }) {
@@ -31313,7 +32536,7 @@ function MediaMenu({ api }) {
31313
32536
  return /* @__PURE__ */ jsxs2("span", { ref: rootRef, style: { position: "relative", display: "inline-block" }, children: [
31314
32537
  /* @__PURE__ */ jsx2("button", { title: "Insert online video", style: btnStyle(open), onMouseDown: (event) => event.preventDefault(), onClick: () => setOpen(!open), children: "Media" }),
31315
32538
  open && /* @__PURE__ */ jsxs2("div", { style: { position: "absolute", top: 28, right: 0, zIndex: 100, width: 300, padding: 10, display: "grid", gap: 7, background: T.popoverBg, border: `1px solid ${T.border}`, borderRadius: 8, boxShadow: T.popoverShadow }, children: [
31316
- /* @__PURE__ */ jsx2("input", { "aria-label": "Online video URL", value: url, onChange: (event) => setUrl(event.target.value), placeholder: "https://www.youtube.com/watch?v=\u2026", style: { width: "100%", boxSizing: "border-box", border: `1px solid ${T.border}`, borderRadius: 6, padding: "6px 8px", font: "13px system-ui, sans-serif", color: T.fg, background: T.popoverBg } }),
32539
+ /* @__PURE__ */ jsx2("input", { "aria-label": "Online video URL", type: "url", value: url, onChange: (event) => setUrl(event.target.value), placeholder: "https://www.youtube.com/watch?v=\u2026", style: { width: "100%", boxSizing: "border-box", border: `1px solid ${T.border}`, borderRadius: 6, padding: "6px 8px", font: "13px system-ui, sans-serif", color: T.fg, background: T.popoverBg } }),
31317
32540
  /* @__PURE__ */ jsx2(
31318
32541
  "button",
31319
32542
  {
@@ -31583,6 +32806,65 @@ function TableMenu({ api }) {
31583
32806
  )
31584
32807
  ] });
31585
32808
  }
32809
+ function TableFormatTab({
32810
+ api,
32811
+ fill,
32812
+ onChanged
32813
+ }) {
32814
+ const run = (op) => {
32815
+ api?.tableOp(op);
32816
+ onChanged();
32817
+ };
32818
+ return /* @__PURE__ */ jsxs2("span", { "data-dxw-table-format": "", style: { display: "contents" }, children: [
32819
+ /* @__PURE__ */ jsx2(
32820
+ ColorMenu,
32821
+ {
32822
+ current: fill ?? "#FFFFFF",
32823
+ title: "Cell fill color",
32824
+ trigger: /* @__PURE__ */ jsxs2(Fragment2, { children: [
32825
+ /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", style: { width: 14, height: 14, borderRadius: 3, border: `1px solid ${T.border}`, background: fill ?? "#FFFFFF" } }),
32826
+ "Cell fill"
32827
+ ] }),
32828
+ onPick: (color) => run({ kind: "cellShading", fill: color })
32829
+ }
32830
+ ),
32831
+ fill !== null && /* @__PURE__ */ jsx2(Btn, { label: "No fill", title: "Remove cell fill", onClick: () => run({ kind: "cellShading", fill: null }) }),
32832
+ /* @__PURE__ */ jsx2(
32833
+ ActionMenu,
32834
+ {
32835
+ label: "Cell alignment",
32836
+ title: "Align text inside the current cell",
32837
+ width: 132,
32838
+ groups: [{ items: [["top", "Top"], ["center", "Middle"], ["bottom", "Bottom"]] }],
32839
+ onPick: (value) => run({ kind: "cellVAlign", v: value })
32840
+ }
32841
+ ),
32842
+ /* @__PURE__ */ jsx2(
32843
+ ActionMenu,
32844
+ {
32845
+ label: "Rows & columns",
32846
+ title: "Edit rows and columns around the current cell",
32847
+ width: 144,
32848
+ groups: [
32849
+ { label: "Rows", items: [["rowAbove", "Insert row above"], ["rowBelow", "Insert row below"], ["deleteRow", "Delete row"]] },
32850
+ { label: "Columns", items: [["colLeft", "Insert column left"], ["colRight", "Insert column right"], ["deleteCol", "Delete column"]] }
32851
+ ],
32852
+ onPick: (value) => run(value)
32853
+ }
32854
+ ),
32855
+ /* @__PURE__ */ jsx2(
32856
+ ActionMenu,
32857
+ {
32858
+ label: "Merge",
32859
+ title: "Merge or split the current cell",
32860
+ width: 78,
32861
+ groups: [{ items: [["mergeRight", "Merge right"], ["mergeDown", "Merge down"], ["splitCell", "Split cell"]] }],
32862
+ onPick: (value) => run(value)
32863
+ }
32864
+ ),
32865
+ /* @__PURE__ */ jsx2(Btn, { label: "Delete table", title: "Delete the current table", onClick: () => run("deleteTable") })
32866
+ ] });
32867
+ }
31586
32868
  function PagePreview({
31587
32869
  kind,
31588
32870
  width = 8.5,
@@ -31592,6 +32874,7 @@ function PagePreview({
31592
32874
  columns,
31593
32875
  columnSeparator,
31594
32876
  border,
32877
+ borderColor,
31595
32878
  lineNumbers
31596
32879
  }) {
31597
32880
  const maxWidth = mirrored ? 21 : 34;
@@ -31602,7 +32885,7 @@ function PagePreview({
31602
32885
  const papers = mirrored ? 2 : 1;
31603
32886
  const inset = margins ?? [0.8, 0.8, 0.8, 0.8];
31604
32887
  const borderWidth = border === "thick" ? 2 : border === "none" ? 0 : 1;
31605
- const borderColor = border === "accent" ? T.accent : T.muted;
32888
+ const pageBorderColor = borderColor ?? (border === "accent" ? T.accent : T.muted);
31606
32889
  return /* @__PURE__ */ jsx2(
31607
32890
  "span",
31608
32891
  {
@@ -31621,7 +32904,7 @@ function PagePreview({
31621
32904
  width: paperWidth,
31622
32905
  height: paperHeight,
31623
32906
  background: "var(--dxw-layout-preview-bg, #fff)",
31624
- border: `${Math.max(1, borderWidth)}px solid ${borderWidth ? borderColor : T.border}`
32907
+ border: `${Math.max(1, borderWidth)}px solid ${borderWidth ? pageBorderColor : T.border}`
31625
32908
  },
31626
32909
  children: [
31627
32910
  kind === "margins" && /* @__PURE__ */ jsx2("span", { style: {
@@ -31950,8 +33233,9 @@ function MarginMenu({
31950
33233
  "input",
31951
33234
  {
31952
33235
  "aria-label": `${label} margin (inches)`,
31953
- type: "text",
31954
- inputMode: "decimal",
33236
+ type: "number",
33237
+ min: "0",
33238
+ step: "0.05",
31955
33239
  required: true,
31956
33240
  autoFocus: side === "top",
31957
33241
  value: values[side],
@@ -32076,8 +33360,9 @@ function PageSizeMenu({
32076
33360
  "input",
32077
33361
  {
32078
33362
  "aria-label": `Page ${side} (inches)`,
32079
- type: "text",
32080
- inputMode: "decimal",
33363
+ type: "number",
33364
+ min: "0.1",
33365
+ step: "0.05",
32081
33366
  required: true,
32082
33367
  autoFocus: side === "width",
32083
33368
  value: values[side],
@@ -32157,6 +33442,136 @@ function PageSizeMenu({
32157
33442
  )
32158
33443
  ] });
32159
33444
  }
33445
+ function PageBorderMenu({
33446
+ scope,
33447
+ onApply,
33448
+ open,
33449
+ onOpenChange
33450
+ }) {
33451
+ const [customOpen, setCustomOpen] = useState2(false);
33452
+ const [color, setColor] = useState2("#4472c4");
33453
+ const [widthPt, setWidthPt] = useState2("1");
33454
+ const rootRef = useRef2(null);
33455
+ const [dialogPosition, setDialogPosition] = useState2({ left: 8, top: 8 });
33456
+ useEffect2(() => {
33457
+ if (!customOpen) return;
33458
+ const positionDialog = () => {
33459
+ const trigger = rootRef.current?.querySelector("[data-dxw-layout-menu-trigger]");
33460
+ if (!trigger) return;
33461
+ const rect = trigger.getBoundingClientRect();
33462
+ const width2 = Math.min(244, window.innerWidth - 16);
33463
+ setDialogPosition({
33464
+ left: Math.max(8, Math.min(rect.left, window.innerWidth - width2 - 8)),
33465
+ top: Math.max(8, Math.min(rect.bottom + 4, window.innerHeight - 224))
33466
+ });
33467
+ };
33468
+ const close = (event) => {
33469
+ if (!rootRef.current?.contains(event.target)) setCustomOpen(false);
33470
+ };
33471
+ const keydown = (event) => {
33472
+ if (event.key === "Escape") setCustomOpen(false);
33473
+ };
33474
+ positionDialog();
33475
+ document.addEventListener("mousedown", close);
33476
+ document.addEventListener("keydown", keydown);
33477
+ window.addEventListener("resize", positionDialog);
33478
+ return () => {
33479
+ document.removeEventListener("mousedown", close);
33480
+ document.removeEventListener("keydown", keydown);
33481
+ window.removeEventListener("resize", positionDialog);
33482
+ };
33483
+ }, [customOpen]);
33484
+ const validColor = normalizedColor(color);
33485
+ const width = Number(widthPt);
33486
+ const valid = validColor !== null && Number.isFinite(width) && width >= 0.25 && width <= 12;
33487
+ const applyCustom = () => {
33488
+ if (!validColor || !valid) return;
33489
+ onApply({ pageBorders: { sz: Math.round(width * 8), color: validColor } });
33490
+ setCustomOpen(false);
33491
+ };
33492
+ const pick = (value) => {
33493
+ if (value === "custom") {
33494
+ setCustomOpen(true);
33495
+ return;
33496
+ }
33497
+ setCustomOpen(false);
33498
+ if (value === "none") onApply({ pageBorders: null });
33499
+ else if (value === "thin") onApply({ pageBorders: { sz: 4 } });
33500
+ else if (value === "thick") onApply({ pageBorders: { sz: 12 } });
33501
+ else onApply({ pageBorders: { sz: 8, color: "4472C4" } });
33502
+ };
33503
+ return /* @__PURE__ */ jsxs2("span", { ref: rootRef, style: { position: "relative", display: "inline-flex" }, children: [
33504
+ /* @__PURE__ */ jsx2(
33505
+ LayoutMenu,
33506
+ {
33507
+ name: "page-border",
33508
+ label: "Page border",
33509
+ open,
33510
+ onOpenChange,
33511
+ onPick: pick,
33512
+ options: [
33513
+ { value: "none", label: "None", description: "No page border", preview: /* @__PURE__ */ jsx2(PagePreview, { kind: "page-border", border: "none" }) },
33514
+ { value: "thin", label: "Thin box", description: "\xBD pt solid line", preview: /* @__PURE__ */ jsx2(PagePreview, { kind: "page-border", border: "thin" }) },
33515
+ { value: "thick", label: "Thick box", description: "1\xBD pt solid line", preview: /* @__PURE__ */ jsx2(PagePreview, { kind: "page-border", border: "thick" }) },
33516
+ { value: "accent", label: "Accent box", description: "Blue 1 pt line", preview: /* @__PURE__ */ jsx2(PagePreview, { kind: "page-border", border: "accent" }) },
33517
+ { value: "custom", label: "Custom border\u2026", description: "Choose a color and line weight", preview: /* @__PURE__ */ jsx2(PagePreview, { kind: "page-border", border: "accent", borderColor: validColor ?? "#4472c4" }) }
33518
+ ]
33519
+ }
33520
+ ),
33521
+ customOpen && /* @__PURE__ */ jsxs2(
33522
+ "div",
33523
+ {
33524
+ role: "dialog",
33525
+ "aria-label": "Custom Page Border",
33526
+ onMouseDown: (event) => event.stopPropagation(),
33527
+ style: {
33528
+ position: "fixed",
33529
+ top: dialogPosition.top,
33530
+ left: dialogPosition.left,
33531
+ zIndex: 201,
33532
+ width: "min(224px, calc(100vw - 16px))",
33533
+ boxSizing: "border-box",
33534
+ background: T.popoverBg,
33535
+ border: `1px solid ${T.border}`,
33536
+ borderRadius: 8,
33537
+ boxShadow: T.popoverShadow,
33538
+ padding: 10,
33539
+ display: "grid",
33540
+ gap: 8
33541
+ },
33542
+ children: [
33543
+ /* @__PURE__ */ jsx2("strong", { style: { fontSize: 13 }, children: "Custom Page Border" }),
33544
+ /* @__PURE__ */ jsxs2("label", { style: { display: "grid", gridTemplateColumns: "54px 1fr", gap: 8, alignItems: "center", fontSize: 12 }, children: [
33545
+ /* @__PURE__ */ jsx2("span", { children: "Color" }),
33546
+ /* @__PURE__ */ jsxs2("span", { style: { display: "flex", gap: 6 }, children: [
33547
+ /* @__PURE__ */ jsx2("input", { "aria-label": "Page border color picker", type: "color", value: validColor ?? "#4472c4", onChange: (event) => setColor(event.target.value), style: { width: 34, height: 28, padding: 1, border: `1px solid ${T.border}`, borderRadius: 5, background: T.popoverBg } }),
33548
+ /* @__PURE__ */ jsx2("input", { "aria-label": "Page border color", autoFocus: true, value: color, onChange: (event) => setColor(event.target.value), onKeyDown: (event) => event.key === "Enter" && applyCustom(), spellCheck: false, style: { width: 92, boxSizing: "border-box", border: `1px solid ${T.border}`, borderRadius: 5, padding: "4px 6px", color: T.fg, background: T.popoverBg } })
33549
+ ] })
33550
+ ] }),
33551
+ /* @__PURE__ */ jsxs2("label", { style: { display: "grid", gridTemplateColumns: "54px 1fr", gap: 8, alignItems: "center", fontSize: 12 }, children: [
33552
+ /* @__PURE__ */ jsx2("span", { children: "Weight" }),
33553
+ /* @__PURE__ */ jsxs2("select", { "aria-label": "Page border width", value: widthPt, onChange: (event) => setWidthPt(event.target.value), style: { width: 132, border: `1px solid ${T.border}`, borderRadius: 5, padding: "4px 6px", color: T.fg, background: T.popoverBg }, children: [
33554
+ /* @__PURE__ */ jsx2("option", { value: "0.5", children: "\xBD pt" }),
33555
+ /* @__PURE__ */ jsx2("option", { value: "1", children: "1 pt" }),
33556
+ /* @__PURE__ */ jsx2("option", { value: "1.5", children: "1\xBD pt" }),
33557
+ /* @__PURE__ */ jsx2("option", { value: "2.25", children: "2\xBC pt" }),
33558
+ /* @__PURE__ */ jsx2("option", { value: "3", children: "3 pt" })
33559
+ ] })
33560
+ ] }),
33561
+ /* @__PURE__ */ jsxs2("span", { style: { color: T.muted, fontSize: 11 }, children: [
33562
+ "Applies to ",
33563
+ scope === "section" ? "this section" : "the whole document",
33564
+ "."
33565
+ ] }),
33566
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "flex-end", gap: 6 }, children: [
33567
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => setCustomOpen(false), style: { ...pillBtn, background: T.popoverBg, color: T.fg }, children: "Cancel" }),
33568
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: applyCustom, disabled: !valid, style: pillBtn, children: "Apply" })
33569
+ ] })
33570
+ ]
33571
+ }
33572
+ )
33573
+ ] });
33574
+ }
32160
33575
  function LayoutTab({ api, showArrange }) {
32161
33576
  const [scope, setScope] = useState2("document");
32162
33577
  const [section, setSection] = useState2(null);
@@ -32252,26 +33667,7 @@ function LayoutTab({ api, showArrange }) {
32252
33667
  onPick: (value) => set({ columns: parseInt(value, 10), columnSeparator: value === "2-divider" })
32253
33668
  }
32254
33669
  ),
32255
- /* @__PURE__ */ jsx2(
32256
- LayoutMenu,
32257
- {
32258
- name: "page-border",
32259
- label: "Page border",
32260
- ...menuState("page-border"),
32261
- options: [
32262
- { value: "none", label: "None", description: "No page border", preview: /* @__PURE__ */ jsx2(PagePreview, { kind: "page-border", border: "none" }) },
32263
- { value: "thin", label: "Thin box", description: "\xBD pt solid line", preview: /* @__PURE__ */ jsx2(PagePreview, { kind: "page-border", border: "thin" }) },
32264
- { value: "thick", label: "Thick box", description: "1\xBD pt solid line", preview: /* @__PURE__ */ jsx2(PagePreview, { kind: "page-border", border: "thick" }) },
32265
- { value: "accent", label: "Accent box", description: "Blue 1 pt line", preview: /* @__PURE__ */ jsx2(PagePreview, { kind: "page-border", border: "accent" }) }
32266
- ],
32267
- onPick: (value) => {
32268
- if (value === "none") set({ pageBorders: null });
32269
- else if (value === "thin") set({ pageBorders: { sz: 4 } });
32270
- else if (value === "thick") set({ pageBorders: { sz: 12 } });
32271
- else set({ pageBorders: { sz: 8, color: "4472C4" } });
32272
- }
32273
- }
32274
- ),
33670
+ /* @__PURE__ */ jsx2(PageBorderMenu, { scope, onApply: set, ...menuState("page-border") }),
32275
33671
  /* @__PURE__ */ jsx2(
32276
33672
  LayoutMenu,
32277
33673
  {
@@ -32336,6 +33732,125 @@ function LayoutTab({ api, showArrange }) {
32336
33732
  }
32337
33733
  );
32338
33734
  }
33735
+ function SmartArtTextControls({ api, nodeIndex }) {
33736
+ const nodeSelected = nodeIndex !== void 0;
33737
+ const [format, setFormat] = useState2(() => api?.getSelectedSmartArtTextFormat() ?? null);
33738
+ useEffect2(() => setFormat(api?.getSelectedSmartArtTextFormat() ?? null), [api, nodeIndex]);
33739
+ if (!format) return null;
33740
+ const apply = (patch) => {
33741
+ if (api?.setSelectedSmartArtTextFormat(patch)) setFormat(api.getSelectedSmartArtTextFormat());
33742
+ };
33743
+ const fonts = detectFonts();
33744
+ const sizes = SIZES.includes(format.fontSizePt) ? SIZES : [format.fontSizePt, ...SIZES];
33745
+ const scope = nodeSelected ? "selected node" : "all nodes";
33746
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
33747
+ /* @__PURE__ */ jsx2(Sep, {}),
33748
+ /* @__PURE__ */ jsx2(
33749
+ ToolbarMenuSelect,
33750
+ {
33751
+ title: `Font for ${scope}`,
33752
+ value: format.fontFamily,
33753
+ width: 126,
33754
+ menuWidth: 210,
33755
+ options: (fonts.includes(format.fontFamily) ? fonts : [format.fontFamily, ...fonts]).map((font) => ({
33756
+ value: font,
33757
+ label: font,
33758
+ fontFamily: font
33759
+ })),
33760
+ onChange: (fontFamily) => fontFamily && apply({ fontFamily })
33761
+ }
33762
+ ),
33763
+ /* @__PURE__ */ jsx2(
33764
+ ToolbarMenuSelect,
33765
+ {
33766
+ title: `Font size for ${scope}`,
33767
+ value: String(format.fontSizePt),
33768
+ width: 58,
33769
+ menuWidth: 92,
33770
+ options: sizes.map((size) => ({ value: String(size), label: String(size) })),
33771
+ onChange: (value) => value && apply({ fontSizePt: Number(value) })
33772
+ }
33773
+ ),
33774
+ /* @__PURE__ */ jsx2(Btn, { label: /* @__PURE__ */ jsx2("b", { children: "B" }), title: `Bold ${scope}`, active: format.bold, onClick: () => apply({ bold: !format.bold }) }),
33775
+ /* @__PURE__ */ jsx2(Btn, { label: /* @__PURE__ */ jsx2("i", { children: "I" }), title: `Italic ${scope}`, active: format.italic, onClick: () => apply({ italic: !format.italic }) }),
33776
+ /* @__PURE__ */ jsx2(
33777
+ ColorMenu,
33778
+ {
33779
+ current: format.color,
33780
+ title: `Text color for ${scope}`,
33781
+ trigger: /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, borderBottom: `3px solid ${format.color}`, padding: "0 3px" }, children: "A" }),
33782
+ onPick: (color) => apply({ color })
33783
+ }
33784
+ ),
33785
+ /* @__PURE__ */ jsx2(
33786
+ ActionMenu,
33787
+ {
33788
+ label: format.alignment === "center" ? "Center" : format.alignment === "right" ? "Right" : "Left",
33789
+ title: `Text alignment for ${scope}`,
33790
+ width: 74,
33791
+ groups: [{ items: [["left", "Align left"], ["center", "Align center"], ["right", "Align right"]] }],
33792
+ onPick: (alignment) => apply({ alignment })
33793
+ }
33794
+ )
33795
+ ] });
33796
+ }
33797
+ function ObjectFormatTab({
33798
+ api,
33799
+ context,
33800
+ showArrange
33801
+ }) {
33802
+ const run = (command) => api?.runSelectedObjectCommand(command);
33803
+ const wrap = /* @__PURE__ */ jsx2(
33804
+ ActionMenu,
33805
+ {
33806
+ label: "Wrap",
33807
+ title: "Wrap",
33808
+ width: 72,
33809
+ groups: [{ items: [
33810
+ ["wrapInline", "Inline with text"],
33811
+ ["wrapSquare", "Square"],
33812
+ ["wrapTopAndBottom", "Top and bottom"],
33813
+ ["wrapFront", "In front of text"],
33814
+ ["wrapBehind", "Behind text"]
33815
+ ] }],
33816
+ onPick: (value) => run(value)
33817
+ }
33818
+ );
33819
+ return /* @__PURE__ */ jsxs2("span", { "data-dxw-object-format": "", style: { display: "contents" }, children: [
33820
+ context.kind === "chart" && /* @__PURE__ */ jsx2(ChartMenu, { api, label: "Edit data" }),
33821
+ context.kind === "smartArt" && /* @__PURE__ */ jsx2(SmartArtMenu, { api, label: "Edit SmartArt" }),
33822
+ context.kind === "smartArt" && /* @__PURE__ */ jsx2(SmartArtTextControls, { api, nodeIndex: context.smartArtNodeIndex }),
33823
+ context.canEditText && /* @__PURE__ */ jsx2(
33824
+ Btn,
33825
+ {
33826
+ label: "Edit text",
33827
+ title: context.kind === "smartArt" ? "Edit selected SmartArt node text" : "Edit shape text",
33828
+ onClick: () => run("editText")
33829
+ }
33830
+ ),
33831
+ (context.kind === "shape" || context.kind === "smartArt") && /* @__PURE__ */ jsx2(
33832
+ Btn,
33833
+ {
33834
+ label: context.kind === "smartArt" ? context.smartArtNodeSelected ? "Node fill" : "Fill all" : "Fill",
33835
+ title: context.kind === "smartArt" ? context.smartArtNodeSelected ? "Selected SmartArt node fill" : "All SmartArt node fills" : "Fill color",
33836
+ onClick: () => run("fill")
33837
+ }
33838
+ ),
33839
+ context.kind === "shape" && /* @__PURE__ */ jsx2(Btn, { label: "Outline", title: "Outline color, weight, and style", onClick: () => run("outline") }),
33840
+ context.kind === "line" && /* @__PURE__ */ jsx2(Btn, { label: "Line style", title: "Line color, weight, and style", onClick: () => run("lineStyle") }),
33841
+ (context.kind === "image" || context.kind === "model3d") && /* @__PURE__ */ jsx2(Btn, { label: "Alt text", title: "Alternative text", onClick: () => run("altText") }),
33842
+ wrap,
33843
+ /* @__PURE__ */ jsx2(Btn, { label: "Size", title: "Exact size", onClick: () => run("size") }),
33844
+ /* @__PURE__ */ jsx2(Btn, { label: "Position", title: "Exact page position", onClick: () => run("position") }),
33845
+ (context.kind === "shape" || context.kind === "line" || context.kind === "image" || context.kind === "model3d") && /* @__PURE__ */ jsx2(Btn, { label: "Rotate", title: "Set rotation", onClick: () => run("rotate") }),
33846
+ showArrange && /* @__PURE__ */ jsxs2(Fragment2, { children: [
33847
+ /* @__PURE__ */ jsx2(Btn, { label: "Bring forward", title: "Bring selected object forward", onClick: () => run("bringForward") }),
33848
+ /* @__PURE__ */ jsx2(Btn, { label: "Send backward", title: "Send selected object backward", onClick: () => run("sendBackward") })
33849
+ ] }),
33850
+ context.kind === "model3d" && /* @__PURE__ */ jsx2(Btn, { label: "Reset 3D", title: "Reset 3D rotation", onClick: () => run("reset3d") }),
33851
+ /* @__PURE__ */ jsx2(Btn, { label: "Delete", title: "Delete selected object", onClick: () => run("delete") })
33852
+ ] });
33853
+ }
32339
33854
  function DocxToolbar({
32340
33855
  api,
32341
33856
  onSave,
@@ -32346,6 +33861,14 @@ function DocxToolbar({
32346
33861
  }) {
32347
33862
  const on = (k) => features?.[k] !== false;
32348
33863
  const [tab, setTab] = useState2("home");
33864
+ const priorNormalTab = useRef2("home");
33865
+ const [objectContext, setObjectContext] = useState2(null);
33866
+ const [tableCellFill, setTableCellFill] = useState2(void 0);
33867
+ const tableContextRef = useRef2(void 0);
33868
+ const selectNormalTab = (next) => {
33869
+ priorNormalTab.current = next;
33870
+ setTab(next);
33871
+ };
32349
33872
  const [helpOpen, setHelpOpen] = useState2(false);
32350
33873
  const apple = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
32351
33874
  const shortcut = (key) => apple ? `\u2318${key}` : `Ctrl+${key}`;
@@ -32394,6 +33917,22 @@ function DocxToolbar({
32394
33917
  document.addEventListener("keydown", keydown);
32395
33918
  return () => document.removeEventListener("keydown", keydown);
32396
33919
  }, [features]);
33920
+ useEffect2(() => {
33921
+ const refreshObject = () => {
33922
+ const next = api?.getSelectedObjectContext() ?? null;
33923
+ setObjectContext(next);
33924
+ setTab((current) => {
33925
+ if (next) {
33926
+ if (["home", "insert", "draw", "layout"].includes(current)) priorNormalTab.current = current;
33927
+ return "format";
33928
+ }
33929
+ return current === "format" ? tableContextRef.current !== void 0 ? "tableFormat" : priorNormalTab.current : current;
33930
+ });
33931
+ };
33932
+ refreshObject();
33933
+ document.addEventListener("dxw-object-selection", refreshObject);
33934
+ return () => document.removeEventListener("dxw-object-selection", refreshObject);
33935
+ }, [api]);
32397
33936
  useEffect2(() => {
32398
33937
  const el16 = rootRef.current;
32399
33938
  if (!el16 || typeof ResizeObserver === "undefined") return;
@@ -32418,6 +33957,18 @@ function DocxToolbar({
32418
33957
  setFmt(api?.getSelectionFormat() ?? null);
32419
33958
  setCurStyle(api?.getParagraphStyleId?.() ?? null);
32420
33959
  setListKind(api?.getListType?.() ?? null);
33960
+ const nextTableFill = api?.getTableCellFill();
33961
+ const wasInTable = tableContextRef.current !== void 0;
33962
+ tableContextRef.current = nextTableFill;
33963
+ setTableCellFill(nextTableFill);
33964
+ setTab((current) => {
33965
+ if (nextTableFill !== void 0 && !wasInTable && current !== "format") {
33966
+ if (["home", "insert", "draw", "layout"].includes(current)) priorNormalTab.current = current;
33967
+ return "tableFormat";
33968
+ }
33969
+ if (nextTableFill === void 0 && current === "tableFormat") return priorNormalTab.current;
33970
+ return current;
33971
+ });
32421
33972
  }, [api]);
32422
33973
  useEffect2(() => {
32423
33974
  document.addEventListener("selectionchange", refresh);
@@ -32633,8 +34184,10 @@ function DocxToolbar({
32633
34184
  title: "Exact line height",
32634
34185
  label: "Line height (points)",
32635
34186
  value: "24",
32636
- placeholder: "24",
32637
- submitLabel: "Apply"
34187
+ submitLabel: "Apply",
34188
+ inputType: "number",
34189
+ min: 1,
34190
+ step: 0.5
32638
34191
  }).then((next) => {
32639
34192
  if (next === null) return;
32640
34193
  const points = Number(next.trim());
@@ -32742,8 +34295,9 @@ function DocxToolbar({
32742
34295
  "button",
32743
34296
  {
32744
34297
  "data-tab": t,
34298
+ "aria-pressed": tab === t,
32745
34299
  onMouseDown: (e) => e.preventDefault(),
32746
- onClick: () => setTab(t),
34300
+ onClick: () => selectNormalTab(t),
32747
34301
  style: {
32748
34302
  border: "none",
32749
34303
  background: tab === t ? T.tabActiveBg : "transparent",
@@ -32758,6 +34312,51 @@ function DocxToolbar({
32758
34312
  },
32759
34313
  t
32760
34314
  )),
34315
+ objectContext && /* @__PURE__ */ jsx2(
34316
+ "button",
34317
+ {
34318
+ "data-tab": "format",
34319
+ "aria-pressed": tab === "format",
34320
+ onMouseDown: (event) => event.preventDefault(),
34321
+ onClick: () => setTab("format"),
34322
+ style: {
34323
+ border: "none",
34324
+ background: tab === "format" ? T.tabActiveBg : "transparent",
34325
+ color: tab === "format" ? T.accent : T.fg,
34326
+ font: "600 12.5px system-ui, sans-serif",
34327
+ padding: "5px 10px",
34328
+ borderRadius: 6,
34329
+ cursor: "pointer"
34330
+ },
34331
+ children: {
34332
+ shape: "Shape Format",
34333
+ line: "Line Format",
34334
+ smartArt: "SmartArt Format",
34335
+ chart: "Chart Format",
34336
+ image: "Picture Format",
34337
+ model3d: "3D Format"
34338
+ }[objectContext.kind]
34339
+ }
34340
+ ),
34341
+ !objectContext && tableCellFill !== void 0 && /* @__PURE__ */ jsx2(
34342
+ "button",
34343
+ {
34344
+ "data-tab": "tableFormat",
34345
+ "aria-pressed": tab === "tableFormat",
34346
+ onMouseDown: (event) => event.preventDefault(),
34347
+ onClick: () => setTab("tableFormat"),
34348
+ style: {
34349
+ border: "none",
34350
+ background: tab === "tableFormat" ? T.tabActiveBg : "transparent",
34351
+ color: tab === "tableFormat" ? T.accent : T.fg,
34352
+ font: "600 12.5px system-ui, sans-serif",
34353
+ padding: "5px 10px",
34354
+ borderRadius: 6,
34355
+ cursor: "pointer"
34356
+ },
34357
+ children: "Table Format"
34358
+ }
34359
+ ),
32761
34360
  on("help") && /* @__PURE__ */ jsx2(
32762
34361
  "button",
32763
34362
  {
@@ -32797,6 +34396,7 @@ function DocxToolbar({
32797
34396
  on("chart") && /* @__PURE__ */ jsx2(ChartMenu, { api }),
32798
34397
  on("media") && /* @__PURE__ */ jsx2(MediaMenu, { api }),
32799
34398
  on("shape") && /* @__PURE__ */ jsx2(ShapeMenu, { api }),
34399
+ on("divider") && /* @__PURE__ */ jsx2(DividerMenu, { api }),
32800
34400
  on("textBox") && /* @__PURE__ */ jsx2(TextBoxMenu, { api }),
32801
34401
  on("wordArt") && /* @__PURE__ */ jsx2(WordArtMenu, { api }),
32802
34402
  on("link") && /* @__PURE__ */ jsx2(LinkMenu, { api }),
@@ -32804,10 +34404,16 @@ function DocxToolbar({
32804
34404
  on("footnote") && /* @__PURE__ */ jsx2(FootnoteMenu, { api }),
32805
34405
  on("bookmark") && /* @__PURE__ */ jsx2(BookmarkMenu, { api }),
32806
34406
  on("crossReference") && /* @__PURE__ */ jsx2(CrossReferenceMenu, { api }),
32807
- on("headerFooter") && /* @__PURE__ */ jsxs2(Fragment2, { children: [
32808
- /* @__PURE__ */ jsx2(Btn, { label: "Header", title: "Edit header", onClick: () => api?.openHeaderFooter("header") }),
32809
- /* @__PURE__ */ jsx2(Btn, { label: "Footer", title: "Edit footer", onClick: () => api?.openHeaderFooter("footer") })
32810
- ] }),
34407
+ on("headerFooter") && /* @__PURE__ */ jsx2(
34408
+ ActionMenu,
34409
+ {
34410
+ label: "Header & footer",
34411
+ title: "Edit the repeating header or footer",
34412
+ width: 118,
34413
+ groups: [{ items: [["header", "Header"], ["footer", "Footer"]] }],
34414
+ onPick: (value) => api?.openHeaderFooter(value)
34415
+ }
34416
+ ),
32811
34417
  /* @__PURE__ */ jsx2(Sep, {}),
32812
34418
  on("pageNumber") && /* @__PURE__ */ jsx2(
32813
34419
  ActionMenu,
@@ -32891,6 +34497,7 @@ function DocxToolbar({
32891
34497
  on("chart") && /* @__PURE__ */ jsx2(ChartMenu, { api }),
32892
34498
  on("media") && /* @__PURE__ */ jsx2(MediaMenu, { api }),
32893
34499
  on("shape") && /* @__PURE__ */ jsx2(ShapeMenu, { api }),
34500
+ on("divider") && /* @__PURE__ */ jsx2(DividerMenu, { api }),
32894
34501
  on("textBox") && /* @__PURE__ */ jsx2(TextBoxMenu, { api }),
32895
34502
  on("wordArt") && /* @__PURE__ */ jsx2(WordArtMenu, { api }),
32896
34503
  on("link") && /* @__PURE__ */ jsx2(LinkMenu, { api }),
@@ -32898,10 +34505,16 @@ function DocxToolbar({
32898
34505
  on("footnote") && /* @__PURE__ */ jsx2(FootnoteMenu, { api }),
32899
34506
  on("bookmark") && /* @__PURE__ */ jsx2(BookmarkMenu, { api }),
32900
34507
  on("crossReference") && /* @__PURE__ */ jsx2(CrossReferenceMenu, { api }),
32901
- on("headerFooter") && /* @__PURE__ */ jsxs2(Fragment2, { children: [
32902
- /* @__PURE__ */ jsx2(Btn, { label: "Header", title: "Edit header", onClick: () => api?.openHeaderFooter("header") }),
32903
- /* @__PURE__ */ jsx2(Btn, { label: "Footer", title: "Edit footer", onClick: () => api?.openHeaderFooter("footer") })
32904
- ] }),
34508
+ on("headerFooter") && /* @__PURE__ */ jsx2(
34509
+ ActionMenu,
34510
+ {
34511
+ label: "Header & footer",
34512
+ title: "Edit the repeating header or footer",
34513
+ width: 118,
34514
+ groups: [{ items: [["header", "Header"], ["footer", "Footer"]] }],
34515
+ onPick: (value) => api?.openHeaderFooter(value)
34516
+ }
34517
+ ),
32905
34518
  on("pageNumber") && /* @__PURE__ */ jsx2(
32906
34519
  ActionMenu,
32907
34520
  {
@@ -32963,6 +34576,8 @@ function DocxToolbar({
32963
34576
  ] })
32964
34577
  ] }),
32965
34578
  mode === "advanced" && tab === "draw" && on("drawing") && /* @__PURE__ */ jsx2(DrawTab, { api }),
34579
+ mode === "advanced" && tab === "format" && objectContext && /* @__PURE__ */ jsx2(ObjectFormatTab, { api, context: objectContext, showArrange: on("arrange") }),
34580
+ mode === "advanced" && tab === "tableFormat" && tableCellFill !== void 0 && /* @__PURE__ */ jsx2(TableFormatTab, { api, fill: tableCellFill, onChanged: refresh }),
32966
34581
  /* @__PURE__ */ jsx2(
32967
34582
  "input",
32968
34583
  {
@@ -33395,7 +35010,8 @@ function DocxView({
33395
35010
  void requestTextInputDialog(anchor, {
33396
35011
  title: "Link address",
33397
35012
  label: "URL",
33398
- value: current.getLinkAt() ?? "https://"
35013
+ value: current.getLinkAt() ?? "https://",
35014
+ inputType: "url"
33399
35015
  }).then((next) => {
33400
35016
  if (next !== null) current.setLink(next.trim() || null);
33401
35017
  });
@@ -33545,12 +35161,13 @@ function DocxView({
33545
35161
  return true;
33546
35162
  },
33547
35163
  insertSymbol: (symbol) => editor?.insertText(symbol) ?? false,
33548
- insertShape: (preset, text) => {
35164
+ insertShape: (preset, text, lineStyle) => {
33549
35165
  const target = insertionTarget();
33550
35166
  if (!target) return false;
33551
35167
  history.checkpoint();
33552
35168
  const drawing = insertShapeAt(doc, target.t, preset, text);
33553
35169
  if (!drawing) return false;
35170
+ if (lineStyle) setDrawingLineStyle(doc, drawing, lineStyle.color, lineStyle.width, lineStyle.dash);
33554
35171
  pages = rerender(doc, void 0, "global");
33555
35172
  editor?.reselectDrawing(drawing);
33556
35173
  return true;
@@ -33580,6 +35197,7 @@ function DocxView({
33580
35197
  editor?.reselectDrawing(source2);
33581
35198
  return true;
33582
35199
  },
35200
+ getSelectedChart: () => editor?.getSelectedChartData() ?? null,
33583
35201
  insertSmartArt: (data) => {
33584
35202
  const target = insertionTarget();
33585
35203
  if (!target) return false;
@@ -33597,6 +35215,7 @@ function DocxView({
33597
35215
  editor?.reselectDrawing(source2);
33598
35216
  return true;
33599
35217
  },
35218
+ getSelectedSmartArt: () => editor?.getSelectedSmartArtData() ?? null,
33600
35219
  insertModel3D: async (file, poster) => {
33601
35220
  const target = insertionTarget();
33602
35221
  if (!target) return false;
@@ -33634,6 +35253,10 @@ function DocxView({
33634
35253
  getDrawingTool: () => editor?.getDrawingTool() ?? null,
33635
35254
  arrangeObject: (action) => editor?.arrangeSelectedObject(action) ?? false,
33636
35255
  hasSelectedObject: () => editor?.hasSelectedObject() ?? false,
35256
+ getSelectedObjectContext: () => editor?.getSelectedObjectContext() ?? null,
35257
+ runSelectedObjectCommand: (command) => editor?.runSelectedObjectCommand(command) ?? false,
35258
+ getSelectedSmartArtTextFormat: () => editor?.getSelectedSmartArtTextFormat() ?? null,
35259
+ setSelectedSmartArtTextFormat: (patch) => editor?.setSelectedSmartArtTextFormat(patch) ?? false,
33637
35260
  addComment: (text) => {
33638
35261
  const segs = editor?.getSelectionSegments() ?? [];
33639
35262
  const segments = segs.length > 0 ? segs : handle ? selectionToSegments(handle.bindings) : [];
@@ -33662,6 +35285,10 @@ function DocxView({
33662
35285
  history.checkpoint();
33663
35286
  if (applyTableOp(doc, caret.t, op)) pages = rerender(doc);
33664
35287
  },
35288
+ getTableCellFill: () => {
35289
+ const caret = editor?.getCaretTarget();
35290
+ return caret ? cellShadingAt(doc, caret.t) : void 0;
35291
+ },
33665
35292
  insertImage: async (file) => {
33666
35293
  const caret = editor?.getCaretTarget();
33667
35294
  if (!caret) return;
@@ -33817,6 +35444,20 @@ function DocxView({
33817
35444
  history.checkpoint();
33818
35445
  if (setParagraphSpacing(doc, targets, patch)) pages = rerender(doc);
33819
35446
  },
35447
+ setParagraphDivider: (divider) => {
35448
+ const segs = editor?.getSelectionSegments() ?? [];
35449
+ const caret = editor?.getCaretTarget();
35450
+ const targets = segs.length > 0 ? segs.map((segment) => segment.t).filter((target) => !!target) : caret ? [caret.t] : [];
35451
+ if (targets.length === 0) return false;
35452
+ history.checkpoint();
35453
+ if (!setParagraphDivider(doc, targets, divider)) return false;
35454
+ pages = rerender(doc);
35455
+ return true;
35456
+ },
35457
+ getParagraphDivider: () => {
35458
+ const target = editor?.getSelectionSegments()?.find((segment) => segment.t)?.t ?? editor?.getCaretTarget()?.t;
35459
+ return target ? paragraphDividerAt(doc, target) : null;
35460
+ },
33820
35461
  setDropCap: (mode, lines = 3) => {
33821
35462
  const target = insertionTarget();
33822
35463
  if (!target) return false;