@shadow-garden/bapbong-editor 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -39,8 +39,95 @@ module.exports = __toCommonJS(index_exports);
39
39
  // packages/editor/src/lib/bapbong-editor.ts
40
40
  var import_prosemirror_model3 = require("prosemirror-model");
41
41
 
42
+ // packages/editor/src/lib/paste-images.ts
43
+ var MAX_DISPLAY_WIDTH = 600;
44
+ function imageFilesIn(dt) {
45
+ const files = [];
46
+ for (const item of Array.from(dt?.items ?? [])) {
47
+ if (item.kind === "file" && item.type.startsWith("image/")) {
48
+ const f = item.getAsFile();
49
+ if (f) files.push(f);
50
+ }
51
+ }
52
+ return files;
53
+ }
54
+ async function bitmapSize(blob) {
55
+ try {
56
+ const bmp = await createImageBitmap(blob);
57
+ const size = { width: bmp.width, height: bmp.height };
58
+ bmp.close();
59
+ return size;
60
+ } catch {
61
+ return null;
62
+ }
63
+ }
64
+ var toDataURL = (blob) => new Promise((resolve, reject) => {
65
+ const r = new FileReader();
66
+ r.onload = () => resolve(r.result);
67
+ r.onerror = () => reject(r.error);
68
+ r.readAsDataURL(blob);
69
+ });
70
+ async function insertImageBlobs(view, blobs) {
71
+ const imageType = view.state.schema.nodes["image"];
72
+ if (!imageType) return false;
73
+ const nodes = [];
74
+ for (const blob of blobs) {
75
+ const size = await bitmapSize(blob);
76
+ if (!size) continue;
77
+ const scale = Math.min(1, MAX_DISPLAY_WIDTH / size.width);
78
+ nodes.push(
79
+ imageType.create({
80
+ src: await toDataURL(blob),
81
+ width: Math.round(size.width * scale),
82
+ height: Math.round(size.height * scale)
83
+ })
84
+ );
85
+ }
86
+ if (!nodes.length) return false;
87
+ let tr = view.state.tr;
88
+ for (const node of nodes) tr = tr.replaceSelectionWith(node);
89
+ view.dispatch(tr.scrollIntoView());
90
+ return true;
91
+ }
92
+ function imagePasteHandler(view, event) {
93
+ const dt = event.clipboardData;
94
+ const files = imageFilesIn(dt);
95
+ if (!files.length) return false;
96
+ const html = dt?.getData("text/html") ?? "";
97
+ if (html) {
98
+ const body = new DOMParser().parseFromString(html, "text/html").body;
99
+ if ((body.textContent ?? "").trim()) return false;
100
+ }
101
+ void insertImageBlobs(view, files);
102
+ return true;
103
+ }
104
+
42
105
  // packages/model/dist/index.js
43
106
  var import_prosemirror_model = require("prosemirror-model");
107
+ function pastedParagraphAttrs(el, heading) {
108
+ const style = el.getAttribute("style") ?? "";
109
+ const m = /(?:^|;)\s*text-align\s*:\s*(center|right|justify)/i.exec(style);
110
+ return { heading, align: m ? m[1].toLowerCase() : null };
111
+ }
112
+ function pastedImageAttrs(el) {
113
+ const e = el;
114
+ const src = e.getAttribute("src") ?? "";
115
+ if (!/^data:image\//i.test(src)) return false;
116
+ const dim = (v) => {
117
+ const n = parseFloat(v ?? "");
118
+ return Number.isFinite(n) && n > 0 ? Math.round(n) : null;
119
+ };
120
+ return {
121
+ src,
122
+ alt: e.getAttribute("alt") ?? "",
123
+ width: dim(e.getAttribute("width")),
124
+ height: dim(e.getAttribute("height"))
125
+ };
126
+ }
127
+ function pastedLinkAttrs(el) {
128
+ const href = (el.getAttribute("href") ?? "").trim();
129
+ return /^(https?:|mailto:|#)/i.test(href) ? { href } : false;
130
+ }
44
131
  var schema = new import_prosemirror_model.Schema({
45
132
  nodes: {
46
133
  doc: {
@@ -76,6 +163,11 @@ var schema = new import_prosemirror_model.Schema({
76
163
  // an <h1>–<h6> in toDOM so the a11y mirror is semantic), or null for a
77
164
  // body paragraph.
78
165
  heading: { default: null },
166
+ // Named Word paragraph style with no outline level: 'Title' |
167
+ // 'Subtitle', or null. Mutually exclusive with `heading` — the
168
+ // setParagraphStyle command is the only writer and keeps the
169
+ // invariant (styleId set ⇒ heading null).
170
+ styleId: { default: null },
79
171
  // w:tabs — [{ pos, val: 'left'|'right'|'center'|'decimal', leader? }]
80
172
  // in px from the paragraph's content left edge, or null. Importer-set.
81
173
  tabs: { default: null },
@@ -84,15 +176,23 @@ var schema = new import_prosemirror_model.Schema({
84
176
  // w:pageBreakBefore — start this paragraph on a new page.
85
177
  pageBreakBefore: { default: false }
86
178
  },
87
- // No getAttrs: nothing in the pipeline parses paragraphs from the DOM
88
- // yet (the importer builds nodes directly). align/indent still round-trip
89
- // out through toDOM. Revisit when HTML paste lands.
90
- parseDOM: [{ tag: "p" }],
179
+ // HTML paste path: recover heading level from h1–h6 and alignment from
180
+ // inline style. Other attrs (list/indent/tabs/spacing) stay importer-only
181
+ // pasted HTML rarely carries them faithfully.
182
+ parseDOM: [
183
+ { tag: "p", getAttrs: (el) => pastedParagraphAttrs(el, null) },
184
+ ...[1, 2, 3, 4, 5, 6].map((level) => ({
185
+ tag: `h${level}`,
186
+ getAttrs: (el) => pastedParagraphAttrs(el, level)
187
+ }))
188
+ ],
91
189
  toDOM(node) {
92
190
  const attrs = node.attrs;
93
191
  const style = paragraphStyle(attrs);
94
192
  const tag = attrs.heading ? `h${attrs.heading}` : "p";
95
- return [tag, style ? { style } : {}, 0];
193
+ const dom = style ? { style } : {};
194
+ if (attrs.styleId) dom["data-style"] = attrs.styleId;
195
+ return [tag, dom, 0];
96
196
  }
97
197
  },
98
198
  text: { group: "inline" },
@@ -132,9 +232,13 @@ var schema = new import_prosemirror_model.Schema({
132
232
  // Paint-only: the layout box stays axis-aligned.
133
233
  rotation: { default: 0 }
134
234
  },
235
+ parseDOM: [{ tag: "img[src]", getAttrs: pastedImageAttrs }],
135
236
  toDOM(node) {
136
237
  const a = node.attrs;
137
- const attrs = { src: a["src"], alt: a["alt"] };
238
+ const attrs = {
239
+ src: a["src"],
240
+ alt: a["alt"]
241
+ };
138
242
  if (a["width"] != null) attrs["width"] = String(a["width"]);
139
243
  if (a["height"] != null) attrs["height"] = String(a["height"]);
140
244
  return ["img", attrs];
@@ -171,7 +275,11 @@ var schema = new import_prosemirror_model.Schema({
171
275
  align: { default: null }
172
276
  },
173
277
  parseDOM: [{ tag: "table" }],
174
- toDOM: (node) => ["table", node.attrs["borders"] ? { "data-borders": "1" } : {}, ["tbody", 0]]
278
+ toDOM: (node) => [
279
+ "table",
280
+ node.attrs["borders"] ? { "data-borders": "1" } : {},
281
+ ["tbody", 0]
282
+ ]
175
283
  },
176
284
  table_row: {
177
285
  content: "table_cell+",
@@ -207,9 +315,12 @@ var schema = new import_prosemirror_model.Schema({
207
315
  parseDOM: [{ tag: "td" }, { tag: "th" }],
208
316
  toDOM(node) {
209
317
  const attrs = {};
210
- if (node.attrs["colspan"] !== 1) attrs["colspan"] = String(node.attrs["colspan"]);
211
- if (node.attrs["rowspan"] !== 1) attrs["rowspan"] = String(node.attrs["rowspan"]);
212
- if (node.attrs["background"]) attrs["style"] = `background-color: ${node.attrs["background"]}`;
318
+ if (node.attrs["colspan"] !== 1)
319
+ attrs["colspan"] = String(node.attrs["colspan"]);
320
+ if (node.attrs["rowspan"] !== 1)
321
+ attrs["rowspan"] = String(node.attrs["rowspan"]);
322
+ if (node.attrs["background"])
323
+ attrs["style"] = `background-color: ${node.attrs["background"]}`;
213
324
  return ["td", attrs, 0];
214
325
  }
215
326
  }
@@ -238,9 +349,15 @@ var schema = new import_prosemirror_model.Schema({
238
349
  // w:color — hex "#RRGGBB"
239
350
  textColor: {
240
351
  attrs: { color: {} },
241
- parseDOM: [{ style: "color", getAttrs: (value) => ({ color: value }) }],
352
+ parseDOM: [
353
+ { style: "color", getAttrs: (value) => ({ color: value }) }
354
+ ],
242
355
  toDOM(mark) {
243
- return ["span", { style: `color: ${mark.attrs["color"]}` }, 0];
356
+ return [
357
+ "span",
358
+ { style: `color: ${mark.attrs["color"]}` },
359
+ 0
360
+ ];
244
361
  }
245
362
  },
246
363
  // w:sz — size in points
@@ -256,7 +373,11 @@ var schema = new import_prosemirror_model.Schema({
256
373
  }
257
374
  ],
258
375
  toDOM(mark) {
259
- return ["span", { style: `font-size: ${mark.attrs["size"]}pt` }, 0];
376
+ return [
377
+ "span",
378
+ { style: `font-size: ${mark.attrs["size"]}pt` },
379
+ 0
380
+ ];
260
381
  }
261
382
  },
262
383
  // w:vertAlign — superscript / subscript
@@ -273,26 +394,51 @@ var schema = new import_prosemirror_model.Schema({
273
394
  highlight: {
274
395
  attrs: { color: {} },
275
396
  parseDOM: [
276
- { style: "background-color", getAttrs: (value) => ({ color: value }) }
397
+ {
398
+ style: "background-color",
399
+ getAttrs: (value) => ({ color: value })
400
+ }
277
401
  ],
278
402
  toDOM(mark) {
279
- return ["span", { style: `background-color: ${mark.attrs["color"]}` }, 0];
403
+ return [
404
+ "span",
405
+ { style: `background-color: ${mark.attrs["color"]}` },
406
+ 0
407
+ ];
280
408
  }
281
409
  },
282
410
  // w:rFonts — font family
283
411
  fontFamily: {
284
412
  attrs: { family: {} },
285
- parseDOM: [{ style: "font-family", getAttrs: (value) => ({ family: value }) }],
413
+ parseDOM: [
414
+ {
415
+ style: "font-family",
416
+ getAttrs: (value) => ({ family: value })
417
+ }
418
+ ],
286
419
  toDOM(mark) {
287
- return ["span", { style: `font-family: ${mark.attrs["family"]}` }, 0];
420
+ return [
421
+ "span",
422
+ { style: `font-family: ${mark.attrs["family"]}` },
423
+ 0
424
+ ];
288
425
  }
289
426
  },
290
427
  // w:hyperlink — external URL or "#anchor"
291
428
  link: {
292
429
  attrs: { href: {} },
293
430
  inclusive: false,
431
+ parseDOM: [{ tag: "a[href]", getAttrs: pastedLinkAttrs }],
294
432
  toDOM(mark) {
295
- return ["a", { href: mark.attrs["href"], rel: "noopener", target: "_blank" }, 0];
433
+ return [
434
+ "a",
435
+ {
436
+ href: mark.attrs["href"],
437
+ rel: "noopener",
438
+ target: "_blank"
439
+ },
440
+ 0
441
+ ];
296
442
  }
297
443
  },
298
444
  // w:footnoteReference — the carrier text is the superscript number; `num`
@@ -306,12 +452,20 @@ var schema = new import_prosemirror_model.Schema({
306
452
  // `el` is an HTMLElement at runtime; this package has no DOM lib, so
307
453
  // narrow structurally rather than naming the type.
308
454
  getAttrs: (el) => ({
309
- num: Number(el.getAttribute("data-footnote")) || 0
455
+ num: Number(
456
+ el.getAttribute(
457
+ "data-footnote"
458
+ )
459
+ ) || 0
310
460
  })
311
461
  }
312
462
  ],
313
463
  toDOM(mark) {
314
- return ["sup", { "data-footnote": String(mark.attrs["num"]) }, 0];
464
+ return [
465
+ "sup",
466
+ { "data-footnote": String(mark.attrs["num"]) },
467
+ 0
468
+ ];
315
469
  }
316
470
  }
317
471
  // The `comment` mark (w:commentRangeStart/End) is contributed by the comment
@@ -324,7 +478,12 @@ var schema = new import_prosemirror_model.Schema({
324
478
  var commentSchema = new import_prosemirror_model.Schema({
325
479
  nodes: {
326
480
  doc: { content: "block+" },
327
- paragraph: { group: "block", content: "inline*", parseDOM: [{ tag: "p" }], toDOM: () => ["p", 0] },
481
+ paragraph: {
482
+ group: "block",
483
+ content: "inline*",
484
+ parseDOM: [{ tag: "p" }],
485
+ toDOM: () => ["p", 0]
486
+ },
328
487
  text: { group: "inline" },
329
488
  // @mention: an inline atom carrying the mentioned user's id + display name.
330
489
  // `leafText` lets textContent include "@Name" (search / plain-text preview).
@@ -345,7 +504,10 @@ var commentSchema = new import_prosemirror_model.Schema({
345
504
  tag: "span.mention",
346
505
  getAttrs: (el) => {
347
506
  const e = el;
348
- return { id: e.getAttribute("data-id"), label: (e.textContent ?? "").replace(/^@/, "") };
507
+ return {
508
+ id: e.getAttribute("data-id"),
509
+ label: (e.textContent ?? "").replace(/^@/, "")
510
+ };
349
511
  }
350
512
  }
351
513
  ]
@@ -367,7 +529,8 @@ function paragraphStyle(attrs) {
367
529
  if (sp) {
368
530
  if (sp.before) parts.push(`margin-top: ${sp.before}px`);
369
531
  if (sp.after) parts.push(`margin-bottom: ${sp.after}px`);
370
- if (sp.line && sp.lineRule === "auto") parts.push(`line-height: ${sp.line}`);
532
+ if (sp.line && sp.lineRule === "auto")
533
+ parts.push(`line-height: ${sp.line}`);
371
534
  else if (sp.line) parts.push(`line-height: ${sp.line}px`);
372
535
  }
373
536
  return parts.join("; ");
@@ -783,11 +946,16 @@ function propsToMarks(p, ctx) {
783
946
  if (p.italic) marks.push(ctx.schema.marks["em"].create());
784
947
  if (p.underline) marks.push(ctx.schema.marks["underline"].create());
785
948
  if (p.strike) marks.push(ctx.schema.marks["strike"].create());
786
- if (p.color) marks.push(ctx.schema.marks["textColor"].create({ color: p.color }));
787
- if (p.sizePt !== void 0) marks.push(ctx.schema.marks["fontSize"].create({ size: p.sizePt }));
788
- if (p.fontFamily) marks.push(ctx.schema.marks["fontFamily"].create({ family: p.fontFamily }));
789
- if (p.highlight) marks.push(ctx.schema.marks["highlight"].create({ color: p.highlight }));
790
- if (p.vertAlign) marks.push(ctx.schema.marks["vertAlign"].create({ value: p.vertAlign }));
949
+ if (p.color)
950
+ marks.push(ctx.schema.marks["textColor"].create({ color: p.color }));
951
+ if (p.sizePt !== void 0)
952
+ marks.push(ctx.schema.marks["fontSize"].create({ size: p.sizePt }));
953
+ if (p.fontFamily)
954
+ marks.push(ctx.schema.marks["fontFamily"].create({ family: p.fontFamily }));
955
+ if (p.highlight)
956
+ marks.push(ctx.schema.marks["highlight"].create({ color: p.highlight }));
957
+ if (p.vertAlign)
958
+ marks.push(ctx.schema.marks["vertAlign"].create({ value: p.vertAlign }));
791
959
  return marks;
792
960
  }
793
961
  var SYMBOL_MAP = {
@@ -807,7 +975,9 @@ function symbolChar(code) {
807
975
  return Number.isNaN(n) ? "" : String.fromCodePoint(n >= 61440 ? n - 61440 + 32 : n);
808
976
  }
809
977
  function hasPageBreak(run) {
810
- return run.children.some((n) => n.name === "w:br" && attrOf(n, "w:type") === "page");
978
+ return run.children.some(
979
+ (n) => n.name === "w:br" && attrOf(n, "w:type") === "page"
980
+ );
811
981
  }
812
982
  function effectiveChildren(nodes) {
813
983
  const out = [];
@@ -856,8 +1026,12 @@ function runInlineNodes(run, marks, ctx) {
856
1026
  if (id && ctx.notes.bodies[kind].has(id)) {
857
1027
  flush();
858
1028
  const num = ctx.notes.ref(kind, id);
859
- const refMarks = [...marks, ctx.schema.marks["vertAlign"].create({ value: "super" })];
860
- if (kind === "footnote") refMarks.push(ctx.schema.marks["footnote"].create({ num }));
1029
+ const refMarks = [
1030
+ ...marks,
1031
+ ctx.schema.marks["vertAlign"].create({ value: "super" })
1032
+ ];
1033
+ if (kind === "footnote")
1034
+ refMarks.push(ctx.schema.marks["footnote"].create({ num }));
861
1035
  out.push(ctx.schema.text(String(num), refMarks));
862
1036
  }
863
1037
  } else if (node.name === "w:br" && attrOf(node, "w:type") !== "page") {
@@ -881,9 +1055,11 @@ function parseAnchorFloat(drawing) {
881
1055
  const posH = child(anchor, "wp:positionH");
882
1056
  if (posH) {
883
1057
  const align = child(posH, "wp:align")?.text.trim();
884
- if (align === "left" || align === "right" || align === "center") float["hAlign"] = align;
1058
+ if (align === "left" || align === "right" || align === "center")
1059
+ float["hAlign"] = align;
885
1060
  const off = emuToPxZero(child(posH, "wp:posOffset")?.text);
886
- if (off !== void 0 && float["hAlign"] === void 0) float["hOffset"] = off;
1061
+ if (off !== void 0 && float["hAlign"] === void 0)
1062
+ float["hOffset"] = off;
887
1063
  const rel = attrOf(posH, "relativeFrom");
888
1064
  float["hRel"] = rel === "page" ? "page" : "margin";
889
1065
  }
@@ -1060,7 +1236,9 @@ function parseShape(run, ctx) {
1060
1236
  function parseTextbox(wsp, ctx) {
1061
1237
  const content = child(child(wsp, "wps:txbx"), "w:txbxContent");
1062
1238
  if (!content) return null;
1063
- const paragraphs = children(content, "w:p").map((p) => parseParagraph(p, ctx).toJSON());
1239
+ const paragraphs = children(content, "w:p").map(
1240
+ (p) => parseParagraph(p, ctx).toJSON()
1241
+ );
1064
1242
  if (paragraphs.length === 0) return null;
1065
1243
  const bodyPr = child(wsp, "wps:bodyPr");
1066
1244
  const ins = (name) => emuToPxZero(attrOf(bodyPr, name));
@@ -1155,7 +1333,10 @@ function flattenOmml(node) {
1155
1333
  function parseParagraph(p, ctx) {
1156
1334
  const pPr = child(p, "w:pPr");
1157
1335
  const pStyleId = attrOf(child(pPr, "w:pStyle"), "w:val");
1158
- const paraBase = mergeRunProps(ctx.styles.docDefaults, ctx.styles.resolveStyle(pStyleId));
1336
+ const paraBase = mergeRunProps(
1337
+ ctx.styles.docDefaults,
1338
+ ctx.styles.resolveStyle(pStyleId)
1339
+ );
1159
1340
  const pPrChain = [
1160
1341
  ctx.styles.docDefaultsPPr,
1161
1342
  ...ctx.styles.resolveStylePPr(pStyleId),
@@ -1188,9 +1369,12 @@ function parseParagraph(p, ctx) {
1188
1369
  } else if (fldType === "end") {
1189
1370
  const kind = fieldKind(field.instr);
1190
1371
  if (kind) {
1191
- inline.push(pageFieldNode(kind, field.resultRuns[0], paraBase, ctx));
1372
+ inline.push(
1373
+ pageFieldNode(kind, field.resultRuns[0], paraBase, ctx)
1374
+ );
1192
1375
  } else {
1193
- for (const r of field.resultRuns) inline.push(...runToInline(r, paraBase, ctx, null));
1376
+ for (const r of field.resultRuns)
1377
+ inline.push(...runToInline(r, paraBase, ctx, null));
1194
1378
  }
1195
1379
  field = null;
1196
1380
  } else if (field.phase === "instr") {
@@ -1207,7 +1391,8 @@ function parseParagraph(p, ctx) {
1207
1391
  if (kind) {
1208
1392
  inline.push(pageFieldNode(kind, resultRuns[0], paraBase, ctx));
1209
1393
  } else {
1210
- for (const r of resultRuns) inline.push(...runToInline(r, paraBase, ctx, null));
1394
+ for (const r of resultRuns)
1395
+ inline.push(...runToInline(r, paraBase, ctx, null));
1211
1396
  }
1212
1397
  } else if (node.name === "w:hyperlink") {
1213
1398
  const rel = attrOf(node, "r:id") ? ctx.rels.get(attrOf(node, "r:id")) : void 0;
@@ -1220,7 +1405,9 @@ function parseParagraph(p, ctx) {
1220
1405
  const text = flattenOmml(node);
1221
1406
  if (text.length > 0) {
1222
1407
  const first = findDescendant(node, "m:r");
1223
- inline.push(ctx.schema.text(text, runMarks(first, paraBase, ctx, null)));
1408
+ inline.push(
1409
+ ctx.schema.text(text, runMarks(first, paraBase, ctx, null))
1410
+ );
1224
1411
  }
1225
1412
  } else if (node.name === "w:commentRangeStart") {
1226
1413
  const id = Number(attrOf(node, "w:id"));
@@ -1237,6 +1424,9 @@ function parseParagraph(p, ctx) {
1237
1424
  if (list) attrs.list = list;
1238
1425
  if (align) attrs.align = align;
1239
1426
  if (heading) attrs.heading = heading;
1427
+ else if (pStyleId && /^(title|subtitle)$/i.test(pStyleId)) {
1428
+ attrs.styleId = pStyleId.toLowerCase() === "title" ? "Title" : "Subtitle";
1429
+ }
1240
1430
  if (indent) attrs.indent = indent;
1241
1431
  if (spacing) attrs.spacing = spacing;
1242
1432
  if (tabs) attrs.tabs = tabs;
@@ -1349,7 +1539,9 @@ function parseList(pPr) {
1349
1539
  return { numId, level: Number.isNaN(ilvl) ? 0 : ilvl };
1350
1540
  }
1351
1541
  function emptyCell(ctx) {
1352
- return ctx.schema.nodes["table_cell"].create(null, [ctx.schema.nodes["paragraph"].create()]);
1542
+ return ctx.schema.nodes["table_cell"].create(null, [
1543
+ ctx.schema.nodes["paragraph"].create()
1544
+ ]);
1353
1545
  }
1354
1546
  function parseCellMargins(tbl) {
1355
1547
  const mar = child(child(tbl, "w:tblPr"), "w:tblCellMar");
@@ -1402,7 +1594,14 @@ function parseBordersEl(bordersEl, sides) {
1402
1594
  }
1403
1595
  return Object.keys(out).length > 0 ? out : null;
1404
1596
  }
1405
- var TABLE_SIDES = ["top", "bottom", "left", "right", "insideH", "insideV"];
1597
+ var TABLE_SIDES = [
1598
+ "top",
1599
+ "bottom",
1600
+ "left",
1601
+ "right",
1602
+ "insideH",
1603
+ "insideV"
1604
+ ];
1406
1605
  var CELL_SIDES = ["top", "bottom", "left", "right"];
1407
1606
  function parseTableBorders(tbl, ctx) {
1408
1607
  const tblPr = child(tbl, "w:tblPr");
@@ -1429,8 +1628,18 @@ function parseTable(tbl, ctx) {
1429
1628
  const vAlign = vAlignVal === "center" || vAlignVal === "bottom" ? vAlignVal : null;
1430
1629
  const borders2 = parseBordersEl(child(tcPr, "w:tcBorders"), CELL_SIDES);
1431
1630
  const content = parseBlocks(tc, ctx);
1432
- if (content.length === 0) content.push(ctx.schema.nodes["paragraph"].create());
1433
- cells.push({ startCol: col, colspan, vMerge, colwidth: widths.length ? widths : null, background, vAlign, borders: borders2, content });
1631
+ if (content.length === 0)
1632
+ content.push(ctx.schema.nodes["paragraph"].create());
1633
+ cells.push({
1634
+ startCol: col,
1635
+ colspan,
1636
+ vMerge,
1637
+ colwidth: widths.length ? widths : null,
1638
+ background,
1639
+ vAlign,
1640
+ borders: borders2,
1641
+ content
1642
+ });
1434
1643
  col += colspan;
1435
1644
  }
1436
1645
  return cells;
@@ -1441,12 +1650,17 @@ function parseTable(tbl, ctx) {
1441
1650
  const header = hdr ? attrOf(hdr, "w:val") !== "false" && attrOf(hdr, "w:val") !== "0" : false;
1442
1651
  const trH = child(trPr, "w:trHeight");
1443
1652
  const hv = attrOf(trH, "w:val");
1444
- const height = hv !== void 0 ? { value: twipsToPx(Number(hv)), exact: attrOf(trH, "w:hRule") === "exact" } : null;
1653
+ const height = hv !== void 0 ? {
1654
+ value: twipsToPx(Number(hv)),
1655
+ exact: attrOf(trH, "w:hRule") === "exact"
1656
+ } : null;
1445
1657
  const cs = child(trPr, "w:cantSplit");
1446
1658
  const cantSplit = cs ? attrOf(cs, "w:val") !== "false" && attrOf(cs, "w:val") !== "0" : false;
1447
1659
  return { header, height, cantSplit };
1448
1660
  });
1449
- const colIndex = logicalRows.map((cells) => new Map(cells.map((c) => [c.startCol, c])));
1661
+ const colIndex = logicalRows.map(
1662
+ (cells) => new Map(cells.map((c) => [c.startCol, c]))
1663
+ );
1450
1664
  const rows = logicalRows.map((cells, r) => {
1451
1665
  const emitted = [];
1452
1666
  for (const cell of cells) {
@@ -1487,7 +1701,8 @@ function parseTable(tbl, ctx) {
1487
1701
  const attrs = {};
1488
1702
  if (cellPadding) attrs["cellPadding"] = cellPadding;
1489
1703
  if (borders) attrs["borders"] = borders;
1490
- if (jc === "center" || jc === "right" || jc === "end") attrs["align"] = jc === "end" ? "right" : jc;
1704
+ if (jc === "center" || jc === "right" || jc === "end")
1705
+ attrs["align"] = jc === "end" ? "right" : jc;
1491
1706
  return ctx.schema.nodes["table"].create(
1492
1707
  Object.keys(attrs).length > 0 ? attrs : null,
1493
1708
  rows.length > 0 ? rows : [ctx.schema.nodes["table_row"].create(null, [emptyCell(ctx)])]
@@ -1553,18 +1768,27 @@ async function readPartRels(zip, partPath) {
1553
1768
  return xml ? parseXml(xml) : void 0;
1554
1769
  }
1555
1770
  async function buildNotesRegistry(zip) {
1556
- const bodies = { footnote: /* @__PURE__ */ new Map(), endnote: /* @__PURE__ */ new Map() };
1771
+ const bodies = {
1772
+ footnote: /* @__PURE__ */ new Map(),
1773
+ endnote: /* @__PURE__ */ new Map()
1774
+ };
1557
1775
  const load = async (path, root, tag, into) => {
1558
1776
  const xml = await readPart(zip, path);
1559
1777
  if (!xml) return;
1560
1778
  for (const note of children(child(parseXml(xml), root), tag)) {
1561
1779
  const id = attrOf(note, "w:id");
1562
1780
  const type = attrOf(note, "w:type");
1563
- if (id === void 0 || Number(id) < 1 || type && type !== "normal") continue;
1781
+ if (id === void 0 || Number(id) < 1 || type && type !== "normal")
1782
+ continue;
1564
1783
  into.set(id, note);
1565
1784
  }
1566
1785
  };
1567
- await load("word/footnotes.xml", "w:footnotes", "w:footnote", bodies.footnote);
1786
+ await load(
1787
+ "word/footnotes.xml",
1788
+ "w:footnotes",
1789
+ "w:footnote",
1790
+ bodies.footnote
1791
+ );
1568
1792
  await load("word/endnotes.xml", "w:endnotes", "w:endnote", bodies.endnote);
1569
1793
  const reg = {
1570
1794
  bodies,
@@ -1599,7 +1823,10 @@ async function buildCommentsRegistry(zip) {
1599
1823
  const ext = /* @__PURE__ */ new Map();
1600
1824
  const extXml = await readPart(zip, "word/commentsExtended.xml");
1601
1825
  if (extXml) {
1602
- for (const ex of children(child(parseXml(extXml), "w15:commentsEx"), "w15:commentEx")) {
1826
+ for (const ex of children(
1827
+ child(parseXml(extXml), "w15:commentsEx"),
1828
+ "w15:commentEx"
1829
+ )) {
1603
1830
  const paraId2 = attrOf(ex, "w15:paraId");
1604
1831
  if (!paraId2) continue;
1605
1832
  const done = attrOf(ex, "w15:done");
@@ -1619,14 +1846,24 @@ function buildCommentsList(ctx) {
1619
1846
  const out = [];
1620
1847
  for (const id of ctx.comments.used) {
1621
1848
  const def = ctx.comments.defs.get(id);
1622
- if (def) out.push({ id, author: def.author, date: def.date, text: collectText(def.body).trim() });
1849
+ if (def)
1850
+ out.push({
1851
+ id,
1852
+ author: def.author,
1853
+ date: def.date,
1854
+ text: collectText(def.body).trim()
1855
+ });
1623
1856
  }
1624
1857
  return out;
1625
1858
  }
1626
1859
  function commentBodyJSON(comment) {
1627
1860
  const paras = children(comment, "w:p").map((p) => {
1628
1861
  const text = collectText(p);
1629
- return commentSchema.node("paragraph", null, text ? [commentSchema.text(text)] : []);
1862
+ return commentSchema.node(
1863
+ "paragraph",
1864
+ null,
1865
+ text ? [commentSchema.text(text)] : []
1866
+ );
1630
1867
  });
1631
1868
  return commentSchema.node("doc", null, paras.length ? paras : [commentSchema.node("paragraph")]).toJSON();
1632
1869
  }
@@ -1646,12 +1883,18 @@ function buildCommentNodes(ctx) {
1646
1883
  if (usedSet.has(cur)) return true;
1647
1884
  return false;
1648
1885
  };
1649
- const ids = [...used.filter((id) => defs.has(id)), ...[...defs.keys()].filter((id) => !usedSet.has(id))];
1886
+ const ids = [
1887
+ ...used.filter((id) => defs.has(id)),
1888
+ ...[...defs.keys()].filter((id) => !usedSet.has(id))
1889
+ ];
1650
1890
  const out = [];
1651
1891
  for (const id of ids) {
1652
1892
  const def = defs.get(id);
1653
1893
  if (!def || !referenced(id)) continue;
1654
- const user = { id: def.author || "unknown", name: def.author || "Unknown" };
1894
+ const user = {
1895
+ id: def.author || "unknown",
1896
+ name: def.author || "Unknown"
1897
+ };
1655
1898
  out.push({
1656
1899
  id,
1657
1900
  parentId: parentOf.get(id) ?? null,
@@ -1665,7 +1908,9 @@ function buildCommentNodes(ctx) {
1665
1908
  }
1666
1909
  function noteBlocks(note, num, ctx) {
1667
1910
  const blocks = parseBlocks(note, ctx);
1668
- const marker = ctx.schema.text(`${num}. `, [ctx.schema.marks["vertAlign"].create({ value: "super" })]);
1911
+ const marker = ctx.schema.text(`${num}. `, [
1912
+ ctx.schema.marks["vertAlign"].create({ value: "super" })
1913
+ ]);
1669
1914
  const first = blocks[0];
1670
1915
  if (first && first.type.name === "paragraph") {
1671
1916
  const kids = [marker];
@@ -1715,7 +1960,10 @@ async function extractMedia(zip) {
1715
1960
  if (!path.startsWith("word/media/")) continue;
1716
1961
  const entry = zip.file(path);
1717
1962
  if (!entry || entry.dir) continue;
1718
- media.set(path, `data:${mimeOf(path)};base64,${await entry.async("base64")}`);
1963
+ media.set(
1964
+ path,
1965
+ `data:${mimeOf(path)};base64,${await entry.async("base64")}`
1966
+ );
1719
1967
  }
1720
1968
  return media;
1721
1969
  }
@@ -1725,11 +1973,16 @@ async function importDocx(input, opts) {
1725
1973
  if (rawDocumentXml === void 0) {
1726
1974
  throw new Error("bapbong-docx: word/document.xml not found in archive");
1727
1975
  }
1728
- const stylesXml = await readPart(zip, "word/styles.xml");
1976
+ const stylesXml2 = await readPart(zip, "word/styles.xml");
1729
1977
  const numberingXml = await readPart(zip, "word/numbering.xml");
1730
1978
  const themeXml = await readPart(zip, "word/theme/theme1.xml");
1731
- const resolveTheme = buildThemeResolver(themeXml ? parseXml(themeXml) : void 0);
1732
- const styles = buildStyleRegistry(stylesXml ? parseXml(stylesXml) : void 0, resolveTheme);
1979
+ const resolveTheme = buildThemeResolver(
1980
+ themeXml ? parseXml(themeXml) : void 0
1981
+ );
1982
+ const styles = buildStyleRegistry(
1983
+ stylesXml2 ? parseXml(stylesXml2) : void 0,
1984
+ resolveTheme
1985
+ );
1733
1986
  const numberingRoot = numberingXml ? parseXml(numberingXml) : void 0;
1734
1987
  const media = await extractMedia(zip);
1735
1988
  const notes = await buildNotesRegistry(zip);
@@ -1778,7 +2031,11 @@ async function importDocx(input, opts) {
1778
2031
  if (!xml) continue;
1779
2032
  const partCtx = makeCtx(buildRels(await readPartRels(zip, partPath)));
1780
2033
  const el = child(parseXml(xml), root);
1781
- store[type] = storyDoc(partCtx, el ? parseBlocks(el, partCtx) : [], ctx.numbering.defs);
2034
+ store[type] = storyDoc(
2035
+ partCtx,
2036
+ el ? parseBlocks(el, partCtx) : [],
2037
+ ctx.numbering.defs
2038
+ );
1782
2039
  }
1783
2040
  };
1784
2041
  await collect("w:headerReference", headers, "w:hdr");
@@ -1851,7 +2108,10 @@ var pxToEmu = (px) => Math.round(px * 9525);
1851
2108
  var commentIdsOf = (node) => node.marks.find((m) => m.type.name === "comment")?.attrs["ids"] ?? [];
1852
2109
  var isInlineLeaf = (node) => node.isText || node.type.name === "image" || node.type.name === "hard_break";
1853
2110
  function esc(s) {
1854
- return s.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
2111
+ return s.replace(
2112
+ /[&<>"]/g,
2113
+ (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]
2114
+ );
1855
2115
  }
1856
2116
  var MIME_EXT = {
1857
2117
  "image/png": "png",
@@ -1874,9 +2134,15 @@ function runProps(marks) {
1874
2134
  const size = byName.get("fontSize")?.attrs["size"];
1875
2135
  if (size != null) out.push(`<w:sz w:val="${Math.round(size * 2)}"/>`);
1876
2136
  const hl = byName.get("highlight")?.attrs["color"];
1877
- if (hl) out.push(`<w:shd w:val="clear" w:color="auto" w:fill="${hl.replace(/^#/, "")}"/>`);
2137
+ if (hl)
2138
+ out.push(
2139
+ `<w:shd w:val="clear" w:color="auto" w:fill="${hl.replace(/^#/, "")}"/>`
2140
+ );
1878
2141
  const va = byName.get("vertAlign")?.attrs["value"];
1879
- if (va) out.push(`<w:vertAlign w:val="${va === "sub" ? "subscript" : "superscript"}"/>`);
2142
+ if (va)
2143
+ out.push(
2144
+ `<w:vertAlign w:val="${va === "sub" ? "subscript" : "superscript"}"/>`
2145
+ );
1880
2146
  return out.length ? `<w:rPr>${out.join("")}</w:rPr>` : "";
1881
2147
  }
1882
2148
  function anchorXml(float, cx, cy, n, graphic) {
@@ -1918,7 +2184,9 @@ function imageXml(node, ctx) {
1918
2184
  const n = ctx.nextId++;
1919
2185
  const rid = `rId${n}`;
1920
2186
  ctx.media.push({ path: `word/media/image${n}.${ext}`, base64: m[2] });
1921
- ctx.rels.push(`<Relationship Id="${rid}" Type="${R_NS}/image" Target="media/image${n}.${ext}"/>`);
2187
+ ctx.rels.push(
2188
+ `<Relationship Id="${rid}" Type="${R_NS}/image" Target="media/image${n}.${ext}"/>`
2189
+ );
1922
2190
  const cx = pxToEmu(node.attrs["width"] ?? 96);
1923
2191
  const cy = pxToEmu(node.attrs["height"] ?? 96);
1924
2192
  return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${n}" name="Picture ${n}"/><a:graphic><a:graphicData uri="${PIC_NS}"><pic:pic><pic:nvPicPr><pic:cNvPr id="${n}" name="image${n}.${ext}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${rid}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm${rotAttr(node)}><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>`;
@@ -1928,7 +2196,8 @@ function inlineXml(node, ctx) {
1928
2196
  if (node.type.name === "image") return imageXml(node, ctx);
1929
2197
  if (node.isText) {
1930
2198
  const fn = node.marks.find((m) => m.type.name === "footnote");
1931
- if (fn) return `<w:r>${runProps(node.marks)}<w:footnoteReference w:id="${fn.attrs["num"]}"/></w:r>`;
2199
+ if (fn)
2200
+ return `<w:r>${runProps(node.marks)}<w:footnoteReference w:id="${fn.attrs["num"]}"/></w:r>`;
1932
2201
  return `<w:r>${runProps(node.marks)}<w:t xml:space="preserve">${esc(node.text ?? "")}</w:t></w:r>`;
1933
2202
  }
1934
2203
  return "";
@@ -1938,9 +2207,12 @@ function inlineUnit(node, ctx) {
1938
2207
  const inner = inlineXml(node, ctx);
1939
2208
  const href = linkHref(node);
1940
2209
  if (!href || !inner) return inner;
1941
- if (href.startsWith("#")) return `<w:hyperlink w:anchor="${esc(href.slice(1))}">${inner}</w:hyperlink>`;
2210
+ if (href.startsWith("#"))
2211
+ return `<w:hyperlink w:anchor="${esc(href.slice(1))}">${inner}</w:hyperlink>`;
1942
2212
  const n = ctx.nextId++;
1943
- ctx.rels.push(`<Relationship Id="rId${n}" Type="${R_NS}/hyperlink" Target="${esc(href)}" TargetMode="External"/>`);
2213
+ ctx.rels.push(
2214
+ `<Relationship Id="rId${n}" Type="${R_NS}/hyperlink" Target="${esc(href)}" TargetMode="External"/>`
2215
+ );
1944
2216
  return `<w:hyperlink r:id="rId${n}">${inner}</w:hyperlink>`;
1945
2217
  }
1946
2218
  function inlineContent(node, ctx) {
@@ -1974,10 +2246,16 @@ function paraProps(node) {
1974
2246
  const a = node.attrs;
1975
2247
  const out = [];
1976
2248
  const heading = a["heading"];
2249
+ const styleId = a["styleId"];
1977
2250
  if (heading) out.push(`<w:pStyle w:val="Heading${heading}"/>`);
2251
+ else if (styleId === "Title" || styleId === "Subtitle")
2252
+ out.push(`<w:pStyle w:val="${styleId}"/>`);
1978
2253
  if (a["pageBreakBefore"]) out.push("<w:pageBreakBefore/>");
1979
2254
  const list = a["list"];
1980
- if (list) out.push(`<w:numPr><w:ilvl w:val="${list.level}"/><w:numId w:val="${esc(list.numId)}"/></w:numPr>`);
2255
+ if (list)
2256
+ out.push(
2257
+ `<w:numPr><w:ilvl w:val="${list.level}"/><w:numId w:val="${esc(list.numId)}"/></w:numPr>`
2258
+ );
1981
2259
  const sp = a["spacing"];
1982
2260
  if (sp) {
1983
2261
  const at = [];
@@ -1985,7 +2263,9 @@ function paraProps(node) {
1985
2263
  if (sp.after != null) at.push(`w:after="${pxToTwips(sp.after)}"`);
1986
2264
  if (sp.line != null) {
1987
2265
  const auto = sp.lineRule === "auto" || sp.lineRule == null;
1988
- at.push(`w:line="${auto ? Math.round(sp.line * 240) : pxToTwips(sp.line)}"`);
2266
+ at.push(
2267
+ `w:line="${auto ? Math.round(sp.line * 240) : pxToTwips(sp.line)}"`
2268
+ );
1989
2269
  at.push(`w:lineRule="${sp.lineRule ?? "auto"}"`);
1990
2270
  }
1991
2271
  if (at.length) out.push(`<w:spacing ${at.join(" ")}/>`);
@@ -1996,11 +2276,13 @@ function paraProps(node) {
1996
2276
  if (ind.left != null) at.push(`w:left="${pxToTwips(ind.left)}"`);
1997
2277
  if (ind.right != null) at.push(`w:right="${pxToTwips(ind.right)}"`);
1998
2278
  if (ind.hanging != null) at.push(`w:hanging="${pxToTwips(ind.hanging)}"`);
1999
- else if (ind.firstLine != null) at.push(`w:firstLine="${pxToTwips(ind.firstLine)}"`);
2279
+ else if (ind.firstLine != null)
2280
+ at.push(`w:firstLine="${pxToTwips(ind.firstLine)}"`);
2000
2281
  if (at.length) out.push(`<w:ind ${at.join(" ")}/>`);
2001
2282
  }
2002
2283
  const align = a["align"];
2003
- if (align) out.push(`<w:jc w:val="${align === "justify" ? "both" : align}"/>`);
2284
+ if (align)
2285
+ out.push(`<w:jc w:val="${align === "justify" ? "both" : align}"/>`);
2004
2286
  return out.join("");
2005
2287
  }
2006
2288
  function paragraphXml(node, ctx, sectPr = "") {
@@ -2008,7 +2290,14 @@ function paragraphXml(node, ctx, sectPr = "") {
2008
2290
  const pPr = props ? `<w:pPr>${props}</w:pPr>` : "";
2009
2291
  return `<w:p>${pPr}${inlineContent(node, ctx)}</w:p>`;
2010
2292
  }
2011
- var TABLE_SIDES2 = ["top", "bottom", "left", "right", "insideH", "insideV"];
2293
+ var TABLE_SIDES2 = [
2294
+ "top",
2295
+ "bottom",
2296
+ "left",
2297
+ "right",
2298
+ "insideH",
2299
+ "insideV"
2300
+ ];
2012
2301
  var CELL_SIDES2 = ["top", "bottom", "left", "right"];
2013
2302
  var BORDER_STYLE_OUT = {
2014
2303
  solid: "single",
@@ -2030,12 +2319,19 @@ function cellXml(cell, ctx) {
2030
2319
  const a = cell.attrs;
2031
2320
  const pr = [];
2032
2321
  const colwidth = a["colwidth"];
2033
- if (colwidth?.length) pr.push(`<w:tcW w:w="${pxToTwips(colwidth.reduce((x, y) => x + y, 0))}" w:type="dxa"/>`);
2034
- if (a["colspan"] > 1) pr.push(`<w:gridSpan w:val="${a["colspan"]}"/>`);
2322
+ if (colwidth?.length)
2323
+ pr.push(
2324
+ `<w:tcW w:w="${pxToTwips(colwidth.reduce((x, y) => x + y, 0))}" w:type="dxa"/>`
2325
+ );
2326
+ if (a["colspan"] > 1)
2327
+ pr.push(`<w:gridSpan w:val="${a["colspan"]}"/>`);
2035
2328
  const borders = a["borders"];
2036
2329
  if (borders) pr.push(bordersXml("w:tcBorders", borders, CELL_SIDES2));
2037
2330
  const bg = a["background"];
2038
- if (bg) pr.push(`<w:shd w:val="clear" w:color="auto" w:fill="${bg.replace(/^#/, "")}"/>`);
2331
+ if (bg)
2332
+ pr.push(
2333
+ `<w:shd w:val="clear" w:color="auto" w:fill="${bg.replace(/^#/, "")}"/>`
2334
+ );
2039
2335
  const vAlign = a["vAlign"];
2040
2336
  if (vAlign) pr.push(`<w:vAlign w:val="${vAlign}"/>`);
2041
2337
  let content = "";
@@ -2048,7 +2344,10 @@ function rowXml(row, ctx) {
2048
2344
  if (row.attrs["header"]) pr.push("<w:tblHeader/>");
2049
2345
  if (row.attrs["cantSplit"]) pr.push("<w:cantSplit/>");
2050
2346
  const h = row.attrs["height"];
2051
- if (h) pr.push(`<w:trHeight w:val="${pxToTwips(h.value)}" w:hRule="${h.exact ? "exact" : "atLeast"}"/>`);
2347
+ if (h)
2348
+ pr.push(
2349
+ `<w:trHeight w:val="${pxToTwips(h.value)}" w:hRule="${h.exact ? "exact" : "atLeast"}"/>`
2350
+ );
2052
2351
  const trPr = pr.length ? `<w:trPr>${pr.join("")}</w:trPr>` : "";
2053
2352
  let cells = "";
2054
2353
  row.forEach((c) => cells += cellXml(c, ctx));
@@ -2067,7 +2366,9 @@ function tableXml(node, ctx) {
2067
2366
  }
2068
2367
  const firstRow = node.firstChild;
2069
2368
  const grid = [];
2070
- firstRow?.forEach((c) => c.attrs["colwidth"]?.forEach((w) => grid.push(w)));
2369
+ firstRow?.forEach(
2370
+ (c) => c.attrs["colwidth"]?.forEach((w) => grid.push(w))
2371
+ );
2071
2372
  const gridXml = grid.length ? `<w:tblGrid>${grid.map((w) => `<w:gridCol w:w="${pxToTwips(w)}"/>`).join("")}</w:tblGrid>` : "";
2072
2373
  let rows = "";
2073
2374
  node.forEach((r) => rows += rowXml(r, ctx));
@@ -2102,10 +2403,14 @@ function commentBodyXml(body, firstParaId) {
2102
2403
  doc.forEach((p, _o, i) => {
2103
2404
  let runs = "";
2104
2405
  p.forEach((inline) => {
2105
- if (inline.isText) runs += `<w:r><w:t xml:space="preserve">${esc(inline.text ?? "")}</w:t></w:r>`;
2106
- else if (inline.type.name === "mention") runs += `<w:r><w:t xml:space="preserve">@${esc(String(inline.attrs["label"] ?? ""))}</w:t></w:r>`;
2406
+ if (inline.isText)
2407
+ runs += `<w:r><w:t xml:space="preserve">${esc(inline.text ?? "")}</w:t></w:r>`;
2408
+ else if (inline.type.name === "mention")
2409
+ runs += `<w:r><w:t xml:space="preserve">@${esc(String(inline.attrs["label"] ?? ""))}</w:t></w:r>`;
2107
2410
  });
2108
- ps.push(`<w:p${i === 0 ? ` w14:paraId="${firstParaId}"` : ""}>${runs}</w:p>`);
2411
+ ps.push(
2412
+ `<w:p${i === 0 ? ` w14:paraId="${firstParaId}"` : ""}>${runs}</w:p>`
2413
+ );
2109
2414
  });
2110
2415
  return ps.join("") || `<w:p w14:paraId="${firstParaId}"/>`;
2111
2416
  }
@@ -2127,16 +2432,65 @@ function commentsExtendedXml(comments) {
2127
2432
  }
2128
2433
  var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2129
2434
  <Relationships xmlns="${PR_NS}"><Relationship Id="rId1" Type="${R_NS}/officeDocument" Target="word/document.xml"/></Relationships>`;
2435
+ var STYLE_DEFS = (() => {
2436
+ const heading = (level, halfPt) => `<w:style w:type="paragraph" w:styleId="Heading${level}"><w:name w:val="heading ${level}"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:keepNext/><w:spacing w:before="240" w:after="120"/><w:outlineLvl w:val="${level - 1}"/></w:pPr><w:rPr><w:b/><w:sz w:val="${halfPt}"/><w:szCs w:val="${halfPt}"/></w:rPr></w:style>`;
2437
+ return {
2438
+ Heading1: heading(1, 48),
2439
+ Heading2: heading(2, 36),
2440
+ Heading3: heading(3, 28),
2441
+ Heading4: heading(4, 24),
2442
+ Heading5: heading(5, 22),
2443
+ Heading6: heading(6, 22),
2444
+ Title: `<w:style w:type="paragraph" w:styleId="Title"><w:name w:val="Title"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:spacing w:after="80"/></w:pPr><w:rPr><w:sz w:val="56"/><w:szCs w:val="56"/></w:rPr></w:style>`,
2445
+ Subtitle: `<w:style w:type="paragraph" w:styleId="Subtitle"><w:name w:val="Subtitle"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:rPr><w:i/><w:sz w:val="28"/><w:szCs w:val="28"/></w:rPr></w:style>`
2446
+ };
2447
+ })();
2448
+ function usedStyleIds(doc) {
2449
+ const used = /* @__PURE__ */ new Set();
2450
+ doc.descendants((n) => {
2451
+ if (n.type.name !== "paragraph") return;
2452
+ const heading = n.attrs["heading"];
2453
+ const styleId = n.attrs["styleId"];
2454
+ if (heading) used.add(`Heading${heading}`);
2455
+ else if (styleId && STYLE_DEFS[styleId]) used.add(styleId);
2456
+ });
2457
+ return used;
2458
+ }
2459
+ function stylesXml(used) {
2460
+ const defs = [...used].map((id) => STYLE_DEFS[id]).join("");
2461
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2462
+ <w:styles xmlns:w="${W_NS}"><w:docDefaults><w:rPrDefault><w:rPr><w:sz w:val="22"/><w:szCs w:val="22"/></w:rPr></w:rPrDefault><w:pPrDefault/></w:docDefaults><w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style>` + defs + `</w:styles>`;
2463
+ }
2464
+ function mergeStyles(xml, used) {
2465
+ const missing = [...used].filter((id) => !xml.includes(`w:styleId="${id}"`));
2466
+ if (!missing.length) return xml;
2467
+ return xml.replace(
2468
+ "</w:styles>",
2469
+ `${missing.map((id) => STYLE_DEFS[id]).join("")}</w:styles>`
2470
+ );
2471
+ }
2130
2472
  function contentTypes(exts, hasComments) {
2131
2473
  const parts = [
2132
2474
  '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>',
2133
2475
  '<Default Extension="xml" ContentType="application/xml"/>'
2134
2476
  ];
2135
- for (const ext of exts) parts.push(`<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`);
2136
- parts.push('<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>');
2477
+ for (const ext of exts)
2478
+ parts.push(
2479
+ `<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`
2480
+ );
2481
+ parts.push(
2482
+ '<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>'
2483
+ );
2484
+ parts.push(
2485
+ '<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>'
2486
+ );
2137
2487
  if (hasComments) {
2138
- parts.push('<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>');
2139
- parts.push('<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>');
2488
+ parts.push(
2489
+ '<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>'
2490
+ );
2491
+ parts.push(
2492
+ '<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>'
2493
+ );
2140
2494
  }
2141
2495
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2142
2496
  <Types xmlns="${CT_NS}">${parts.join("")}</Types>`;
@@ -2144,17 +2498,30 @@ function contentTypes(exts, hasComments) {
2144
2498
  function mergeContentTypes(xml, exts, hasComments) {
2145
2499
  let out = xml;
2146
2500
  const add = (frag, key) => {
2147
- if (!out.includes(`"${key}"`)) out = out.replace("</Types>", `${frag}</Types>`);
2501
+ if (!out.includes(`"${key}"`))
2502
+ out = out.replace("</Types>", `${frag}</Types>`);
2148
2503
  };
2149
- for (const ext of exts) add(`<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`, ext);
2504
+ for (const ext of exts)
2505
+ add(
2506
+ `<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`,
2507
+ ext
2508
+ );
2150
2509
  if (hasComments) {
2151
- add('<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>', "/word/comments.xml");
2152
- add('<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>', "/word/commentsExtended.xml");
2510
+ add(
2511
+ '<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>',
2512
+ "/word/comments.xml"
2513
+ );
2514
+ add(
2515
+ '<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>',
2516
+ "/word/commentsExtended.xml"
2517
+ );
2153
2518
  }
2154
2519
  return out;
2155
2520
  }
2156
2521
  function extractBodySectPr(xml) {
2157
- const all = xml.match(/<w:sectPr\b[^>]*>[\s\S]*?<\/w:sectPr>|<w:sectPr\b[^>]*\/>/g);
2522
+ const all = xml.match(
2523
+ /<w:sectPr\b[^>]*>[\s\S]*?<\/w:sectPr>|<w:sectPr\b[^>]*\/>/g
2524
+ );
2158
2525
  return all ? all[all.length - 1] : "";
2159
2526
  }
2160
2527
  function mergeRels(xml, newRels) {
@@ -2163,7 +2530,10 @@ function mergeRels(xml, newRels) {
2163
2530
  /<Relationship\b[^>]*Target="comments(?:Extended)?\.xml"[^>]*\/>/g,
2164
2531
  ""
2165
2532
  );
2166
- return base.replace("</Relationships>", `${newRels.join("")}</Relationships>`);
2533
+ return base.replace(
2534
+ "</Relationships>",
2535
+ `${newRels.join("")}</Relationships>`
2536
+ );
2167
2537
  }
2168
2538
  async function exportDocx(doc, opts) {
2169
2539
  const comments = doc.attrs["comments"] ?? [];
@@ -2172,7 +2542,8 @@ async function exportDocx(doc, opts) {
2172
2542
  let idx = 0;
2173
2543
  doc.descendants((n) => {
2174
2544
  if (!isInlineLeaf(n)) return;
2175
- for (const id of commentIdsOf(n)) if (knownComments.has(id)) lastRun.set(id, idx);
2545
+ for (const id of commentIdsOf(n))
2546
+ if (knownComments.has(id)) lastRun.set(id, idx);
2176
2547
  idx++;
2177
2548
  });
2178
2549
  const ctx = {
@@ -2187,13 +2558,20 @@ async function exportDocx(doc, opts) {
2187
2558
  };
2188
2559
  const boundaries = sectionBoundaries(doc);
2189
2560
  let body = "";
2190
- doc.forEach((block, _offset, i) => body += blockXml(block, ctx, boundaries.get(i)));
2561
+ doc.forEach(
2562
+ (block, _offset, i) => body += blockXml(block, ctx, boundaries.get(i))
2563
+ );
2191
2564
  const hasComments = comments.length > 0;
2192
2565
  if (hasComments) {
2193
- ctx.rels.push(`<Relationship Id="rIdComments" Type="${R_NS}/comments" Target="comments.xml"/>`);
2194
- ctx.rels.push(`<Relationship Id="rIdCommentsExt" Type="${R_NS}/commentsExtended" Target="commentsExtended.xml"/>`);
2566
+ ctx.rels.push(
2567
+ `<Relationship Id="rIdComments" Type="${R_NS}/comments" Target="comments.xml"/>`
2568
+ );
2569
+ ctx.rels.push(
2570
+ `<Relationship Id="rIdCommentsExt" Type="${R_NS}/commentsExtended" Target="commentsExtended.xml"/>`
2571
+ );
2195
2572
  }
2196
2573
  const zip = new import_jszip2.default();
2574
+ const styleIds = usedStyleIds(doc);
2197
2575
  let sectPr = "";
2198
2576
  if (opts?.carry) {
2199
2577
  const carry = opts.carry;
@@ -2201,18 +2579,29 @@ async function exportDocx(doc, opts) {
2201
2579
  if (!f.dir) zip.file(path, await f.async("uint8array"));
2202
2580
  }
2203
2581
  const ct = await carry.file("[Content_Types].xml")?.async("string");
2204
- zip.file("[Content_Types].xml", ct ? mergeContentTypes(ct, ctx.exts, hasComments) : contentTypes(ctx.exts, hasComments));
2582
+ zip.file(
2583
+ "[Content_Types].xml",
2584
+ ct ? mergeContentTypes(ct, ctx.exts, hasComments) : contentTypes(ctx.exts, hasComments)
2585
+ );
2586
+ const carriedStyles = await carry.file("word/styles.xml")?.async("string");
2587
+ if (carriedStyles)
2588
+ zip.file("word/styles.xml", mergeStyles(carriedStyles, styleIds));
2205
2589
  const rels = await carry.file("word/_rels/document.xml.rels")?.async("string");
2206
2590
  zip.file("word/_rels/document.xml.rels", mergeRels(rels, ctx.rels));
2207
2591
  const origDoc = await carry.file("word/document.xml")?.async("string");
2208
2592
  if (origDoc) sectPr = extractBodySectPr(origDoc);
2209
2593
  } else {
2594
+ ctx.rels.push(
2595
+ `<Relationship Id="rIdStyles" Type="${R_NS}/styles" Target="styles.xml"/>`
2596
+ );
2597
+ zip.file("word/styles.xml", stylesXml(styleIds));
2210
2598
  zip.file("[Content_Types].xml", contentTypes(ctx.exts, hasComments));
2211
2599
  zip.file("_rels/.rels", ROOT_RELS);
2212
- if (ctx.rels.length) {
2213
- zip.file("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2214
- <Relationships xmlns="${PR_NS}">${ctx.rels.join("")}</Relationships>`);
2215
- }
2600
+ zip.file(
2601
+ "word/_rels/document.xml.rels",
2602
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2603
+ <Relationships xmlns="${PR_NS}">${ctx.rels.join("")}</Relationships>`
2604
+ );
2216
2605
  }
2217
2606
  const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2218
2607
  <w:document xmlns:w="${W_NS}" xmlns:r="${R_NS}" xmlns:wp="${WP_NS}" xmlns:a="${A_NS}" xmlns:pic="${PIC_NS}" xmlns:wps="${WPS_NS}"><w:body>${body}${sectPr}</w:body></w:document>`;
@@ -2221,14 +2610,27 @@ async function exportDocx(doc, opts) {
2221
2610
  zip.file("word/comments.xml", commentsXml(comments));
2222
2611
  zip.file("word/commentsExtended.xml", commentsExtendedXml(comments));
2223
2612
  }
2224
- for (const { path, base64 } of ctx.media) zip.file(path, base64, { base64: true });
2613
+ for (const { path, base64 } of ctx.media)
2614
+ zip.file(path, base64, { base64: true });
2225
2615
  return zip.generateAsync({ type: "uint8array" });
2226
2616
  }
2227
2617
 
2228
2618
  // packages/layout-engine/dist/index.js
2229
- var DEFAULT_FONT = { family: "Arial", sizePt: 11, bold: false, italic: false };
2619
+ var DEFAULT_FONT = {
2620
+ family: "Arial",
2621
+ sizePt: 11,
2622
+ bold: false,
2623
+ italic: false
2624
+ };
2230
2625
  var PT_TO_PX = 96 / 72;
2231
- var HEADING_PT = { 1: 24, 2: 18, 3: 14, 4: 12, 5: 11, 6: 11 };
2626
+ var HEADING_PT = {
2627
+ 1: 24,
2628
+ 2: 18,
2629
+ 3: 14,
2630
+ 4: 12,
2631
+ 5: 11,
2632
+ 6: 11
2633
+ };
2232
2634
  var LINE_HEIGHT_FACTOR = 1.2;
2233
2635
  var BASELINE_FACTOR = 0.8;
2234
2636
  var DEFAULT_TAB_WIDTH = 48;
@@ -2304,11 +2706,13 @@ function resolveImage(node, pos) {
2304
2706
  function paragraphToFlow(node, base, nodePos, allowFloats = false, marker) {
2305
2707
  const contentStart = nodePos + 1;
2306
2708
  const headingLevel2 = node.attrs["heading"];
2307
- const runBase = headingLevel2 ? { ...base, sizePt: HEADING_PT[headingLevel2] ?? base.sizePt, bold: true } : base;
2709
+ const styleId = node.attrs["styleId"];
2710
+ const runBase = headingLevel2 ? { ...base, sizePt: HEADING_PT[headingLevel2] ?? base.sizePt, bold: true } : styleId === "Title" ? { ...base, sizePt: 28 } : styleId === "Subtitle" ? { ...base, sizePt: 14, italic: true } : base;
2308
2711
  const runs = [];
2309
2712
  const floats = [];
2310
2713
  node.forEach((child2, offset) => {
2311
- if (child2.isText) runs.push(resolveRun(child2, runBase, contentStart + offset));
2714
+ if (child2.isText)
2715
+ runs.push(resolveRun(child2, runBase, contentStart + offset));
2312
2716
  else if (child2.type.name === "image") {
2313
2717
  const float = child2.attrs["float"];
2314
2718
  if (float && allowFloats) {
@@ -2335,7 +2739,8 @@ function paragraphToFlow(node, base, nodePos, allowFloats = false, marker) {
2335
2739
  }
2336
2740
  } else if (child2.type.name === "page_field")
2337
2741
  runs.push(resolveField(child2, runBase, contentStart + offset));
2338
- else if (child2.type.name === "hard_break") runs.push({ break: true, pos: contentStart + offset });
2742
+ else if (child2.type.name === "hard_break")
2743
+ runs.push({ break: true, pos: contentStart + offset });
2339
2744
  });
2340
2745
  const list = node.attrs["list"];
2341
2746
  const align = node.attrs["align"];
@@ -2371,8 +2776,15 @@ function markerFor(node, counter) {
2371
2776
  }
2372
2777
  function nodeToBlock(node, base, nodePos, allowFloats = false, counter) {
2373
2778
  if (node.type.name === "paragraph")
2374
- return paragraphToFlow(node, base, nodePos, allowFloats, markerFor(node, counter));
2375
- if (node.type.name === "table") return tableToFlow(node, base, nodePos, counter);
2779
+ return paragraphToFlow(
2780
+ node,
2781
+ base,
2782
+ nodePos,
2783
+ allowFloats,
2784
+ markerFor(node, counter)
2785
+ );
2786
+ if (node.type.name === "table")
2787
+ return tableToFlow(node, base, nodePos, counter);
2376
2788
  return null;
2377
2789
  }
2378
2790
  function tableToFlow(node, base, nodePos, counter) {
@@ -2384,7 +2796,13 @@ function tableToFlow(node, base, nodePos, counter) {
2384
2796
  const cellPos = rowPos + 1 + cellOffset;
2385
2797
  const content = [];
2386
2798
  cellNode.forEach((child2, childOffset) => {
2387
- const block = nodeToBlock(child2, base, cellPos + 1 + childOffset, true, counter);
2799
+ const block = nodeToBlock(
2800
+ child2,
2801
+ base,
2802
+ cellPos + 1 + childOffset,
2803
+ true,
2804
+ counter
2805
+ );
2388
2806
  if (block) content.push(block);
2389
2807
  });
2390
2808
  const a = cellNode.attrs;
@@ -2416,7 +2834,9 @@ function tableToFlow(node, base, nodePos, counter) {
2416
2834
  }
2417
2835
  function toFlowBlocks(doc, defaultFont = {}, allowFloats = true) {
2418
2836
  const base = { ...DEFAULT_FONT, ...defaultFont };
2419
- const counter = createNumberingCounter(doc.attrs["numbering"]);
2837
+ const counter = createNumberingCounter(
2838
+ doc.attrs["numbering"]
2839
+ );
2420
2840
  const blocks = [];
2421
2841
  doc.forEach((node, offset) => {
2422
2842
  const block = nodeToBlock(node, base, offset, allowFloats, counter);
@@ -2426,7 +2846,16 @@ function toFlowBlocks(doc, defaultFont = {}, allowFloats = true) {
2426
2846
  }
2427
2847
  function tokenizeInline(inline, ctx) {
2428
2848
  if ("break" in inline) {
2429
- return [{ isBreak: true, font: ctx.base, width: 0, isSpace: false, pos: inline.pos, size: 1 }];
2849
+ return [
2850
+ {
2851
+ isBreak: true,
2852
+ font: ctx.base,
2853
+ width: 0,
2854
+ isSpace: false,
2855
+ pos: inline.pos,
2856
+ size: 1
2857
+ }
2858
+ ];
2430
2859
  }
2431
2860
  if ("src" in inline) {
2432
2861
  return [
@@ -2537,7 +2966,12 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2537
2966
  return lineLeft + k * tabWidth - x;
2538
2967
  };
2539
2968
  const tabStops = block.tabs ? [...block.tabs].sort((a, b) => a.pos - b.pos) : [];
2540
- const LEADER_CHARS = { dot: ".", hyphen: "-", underscore: "_", middleDot: "\xB7" };
2969
+ const LEADER_CHARS = {
2970
+ dot: ".",
2971
+ hyphen: "-",
2972
+ underscore: "_",
2973
+ middleDot: "\xB7"
2974
+ };
2541
2975
  const MIN_TAB = 2;
2542
2976
  const tabGroup = (ti) => {
2543
2977
  let width = 0;
@@ -2547,7 +2981,8 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2547
2981
  if (t.isTab) break;
2548
2982
  if (decimalPrefix === null && t.text != null && !t.field) {
2549
2983
  const m = /[.,]/.exec(t.text);
2550
- if (m) decimalPrefix = width + measure(t.text.slice(0, m.index), t.font);
2984
+ if (m)
2985
+ decimalPrefix = width + measure(t.text.slice(0, m.index), t.font);
2551
2986
  }
2552
2987
  width += t.width;
2553
2988
  }
@@ -2670,7 +3105,16 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2670
3105
  height = target;
2671
3106
  }
2672
3107
  const painted = firstLine && marker ? [marker, ...segments] : segments;
2673
- emit({ x: startX, width: lineRight - startX, height, baseline, segments: painted, images, from, to });
3108
+ emit({
3109
+ x: startX,
3110
+ width: lineRight - startX,
3111
+ height,
3112
+ baseline,
3113
+ segments: painted,
3114
+ images,
3115
+ from,
3116
+ to
3117
+ });
2674
3118
  prevTo = to;
2675
3119
  lineTokens = [];
2676
3120
  lineWidth = 0;
@@ -2704,10 +3148,14 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2704
3148
  softWrapped = false;
2705
3149
  continue;
2706
3150
  }
2707
- if (token.isSpace && !token.isTab && lineTokens.length === 0 && softWrapped) continue;
3151
+ if (token.isSpace && !token.isTab && lineTokens.length === 0 && softWrapped)
3152
+ continue;
2708
3153
  if (token.isTab) resolveTab(token, lineStart() + lineWidth, ti);
2709
3154
  if (clusterable(token) && clusterFont && sameFont(clusterFont, token.font)) {
2710
- token.width = Math.max(0, measure(clusterText + token.text, token.font) - clusterWidth);
3155
+ token.width = Math.max(
3156
+ 0,
3157
+ measure(clusterText + token.text, token.font) - clusterWidth
3158
+ );
2711
3159
  }
2712
3160
  const cursor = lineStart() + lineWidth;
2713
3161
  if (!token.isSpace && lineTokens.length > 0 && cursor + token.width > lineRight) {
@@ -2715,14 +3163,21 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2715
3163
  resetCluster();
2716
3164
  softWrapped = true;
2717
3165
  if (token.isTab) resolveTab(token, lineStart() + lineWidth, ti);
2718
- else if (clusterable(token)) token.width = measure(token.text, token.font);
3166
+ else if (clusterable(token))
3167
+ token.width = measure(token.text, token.font);
2719
3168
  }
2720
3169
  if (clusterable(token) && lineTokens.length === 0 && token.text.length > 1 && lineStart() + token.width > lineRight) {
2721
3170
  const avail = lineRight - lineStart();
2722
3171
  let n = 1;
2723
- while (n < token.text.length && measure(token.text.slice(0, n + 1), token.font) <= avail) n++;
3172
+ while (n < token.text.length && measure(token.text.slice(0, n + 1), token.font) <= avail)
3173
+ n++;
2724
3174
  const restText = token.text.slice(n);
2725
- const rest = { ...token, text: restText, width: measure(restText, token.font), size: restText.length };
3175
+ const rest = {
3176
+ ...token,
3177
+ text: restText,
3178
+ width: measure(restText, token.font),
3179
+ size: restText.length
3180
+ };
2726
3181
  if (token.pos != null) rest.pos = token.pos + n;
2727
3182
  token.text = token.text.slice(0, n);
2728
3183
  token.size = n;
@@ -2743,7 +3198,10 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2743
3198
  const rotH = rotatedBoxHeight(token.image);
2744
3199
  maxImagePx = Math.max(maxImagePx, rotH);
2745
3200
  maxAscent = Math.max(maxAscent, token.image.height / 2 + rotH / 2);
2746
- maxDescent = Math.max(maxDescent, Math.max(0, (rotH - token.image.height) / 2));
3201
+ maxDescent = Math.max(
3202
+ maxDescent,
3203
+ Math.max(0, (rotH - token.image.height) / 2)
3204
+ );
2747
3205
  } else {
2748
3206
  maxFontPx = Math.max(maxFontPx, sizePx(token.font));
2749
3207
  if (metrics) {
@@ -2796,7 +3254,13 @@ function shiftTableX(table, dx) {
2796
3254
  }
2797
3255
  var TEXTBOX_INSET = { l: 10, t: 5, r: 10, b: 5 };
2798
3256
  function resolveFloat(f, x, y, ctx) {
2799
- const rf = { x, y, width: f.width, height: f.height, src: f.src };
3257
+ const rf = {
3258
+ x,
3259
+ y,
3260
+ width: f.width,
3261
+ height: f.height,
3262
+ src: f.src
3263
+ };
2800
3264
  if (f.pos != null) rf.pos = f.pos;
2801
3265
  if (f.rotation) rf.rotation = f.rotation;
2802
3266
  if (f.shape) rf.shape = f.shape;
@@ -2842,7 +3306,8 @@ function eachCell(rows, ncols, visit) {
2842
3306
  for (const cell of rows[r].cells) {
2843
3307
  while (col < ncols && spanned[col] > 0) col++;
2844
3308
  visit(r, cell, col);
2845
- for (let k = 0; k < cell.colspan && col + k < ncols; k++) spanned[col + k] = cell.rowspan;
3309
+ for (let k = 0; k < cell.colspan && col + k < ncols; k++)
3310
+ spanned[col + k] = cell.rowspan;
2846
3311
  col += cell.colspan;
2847
3312
  }
2848
3313
  for (let i = 0; i < ncols; i++) if (spanned[i] > 0) spanned[i]--;
@@ -2852,14 +3317,18 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2852
3317
  const avail = contentRight - contentLeft;
2853
3318
  const nrows = table.rows.length;
2854
3319
  const ncols = table.rows.reduce(
2855
- (m, r) => Math.max(m, r.cells.reduce((s, c) => s + c.colspan, 0)),
3320
+ (m, r) => Math.max(
3321
+ m,
3322
+ r.cells.reduce((s, c) => s + c.colspan, 0)
3323
+ ),
2856
3324
  0
2857
3325
  );
2858
3326
  const colWidths = new Array(ncols).fill(0);
2859
3327
  eachCell(table.rows, ncols, (_r, cell, startCol) => {
2860
3328
  if (cell.colwidth && cell.colwidth.length === cell.colspan) {
2861
3329
  for (let k = 0; k < cell.colspan && startCol + k < ncols; k++) {
2862
- if (colWidths[startCol + k] === 0) colWidths[startCol + k] = cell.colwidth[k];
3330
+ if (colWidths[startCol + k] === 0)
3331
+ colWidths[startCol + k] = cell.colwidth[k];
2863
3332
  }
2864
3333
  }
2865
3334
  });
@@ -2867,7 +3336,8 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2867
3336
  const unknown = colWidths.filter((w) => w === 0).length;
2868
3337
  if (unknown > 0) {
2869
3338
  const share = Math.max(0, (avail - known) / unknown);
2870
- for (let i = 0; i < ncols; i++) if (colWidths[i] === 0) colWidths[i] = share;
3339
+ for (let i = 0; i < ncols; i++)
3340
+ if (colWidths[i] === 0) colWidths[i] = share;
2871
3341
  }
2872
3342
  const natural = colWidths.reduce((s, w) => s + w, 0);
2873
3343
  if (natural > avail && natural > 0) {
@@ -2887,9 +3357,15 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2887
3357
  const cellDrafts = [];
2888
3358
  eachCell(table.rows, ncols, (r, cell, col) => {
2889
3359
  let cellWidth = 0;
2890
- for (let k = 0; k < cell.colspan && col + k < ncols; k++) cellWidth += colWidths[col + k];
3360
+ for (let k = 0; k < cell.colspan && col + k < ncols; k++)
3361
+ cellWidth += colWidths[col + k];
2891
3362
  const cellLeft = colX[col];
2892
- const flow = layoutFlow(cell.content, cellLeft + pad.left, cellLeft + cellWidth - pad.right, ctx);
3363
+ const flow = layoutFlow(
3364
+ cell.content,
3365
+ cellLeft + pad.left,
3366
+ cellLeft + cellWidth - pad.right,
3367
+ ctx
3368
+ );
2893
3369
  cellDrafts.push({
2894
3370
  startRow: r,
2895
3371
  startCol: col,
@@ -2909,14 +3385,18 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2909
3385
  const rowHeight = new Array(nrows).fill(0);
2910
3386
  for (const c of cellDrafts) {
2911
3387
  if (c.rowspan === 1) {
2912
- rowHeight[c.startRow] = Math.max(rowHeight[c.startRow], c.contentHeight + pad.top + pad.bottom);
3388
+ rowHeight[c.startRow] = Math.max(
3389
+ rowHeight[c.startRow],
3390
+ c.contentHeight + pad.top + pad.bottom
3391
+ );
2913
3392
  }
2914
3393
  }
2915
3394
  for (const c of cellDrafts) {
2916
3395
  if (c.rowspan > 1) {
2917
3396
  const need = c.contentHeight + pad.top + pad.bottom;
2918
3397
  let span = 0;
2919
- for (let r = c.startRow; r < c.startRow + c.rowspan && r < nrows; r++) span += rowHeight[r];
3398
+ for (let r = c.startRow; r < c.startRow + c.rowspan && r < nrows; r++)
3399
+ span += rowHeight[r];
2920
3400
  if (need > span) {
2921
3401
  const last = Math.min(c.startRow + c.rowspan - 1, nrows - 1);
2922
3402
  rowHeight[last] += need - span;
@@ -2931,7 +3411,8 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2931
3411
  for (let r = 0; r < nrows; r++) rowY[r + 1] = rowY[r] + rowHeight[r];
2932
3412
  const cells = cellDrafts.map((c) => {
2933
3413
  let height = 0;
2934
- for (let r = c.startRow; r < c.startRow + c.rowspan && r < nrows; r++) height += rowHeight[r];
3414
+ for (let r = c.startRow; r < c.startRow + c.rowspan && r < nrows; r++)
3415
+ height += rowHeight[r];
2935
3416
  const slack = Math.max(0, height - pad.top - pad.bottom - c.contentHeight);
2936
3417
  const vOffset = c.vAlign === "bottom" ? slack : c.vAlign === "center" ? slack / 2 : 0;
2937
3418
  const dy = rowY[c.startRow] + pad.top + vOffset;
@@ -2949,19 +3430,30 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2949
3430
  if (c.background) cell.background = c.background;
2950
3431
  if (c.borders) cell.borders = c.borders;
2951
3432
  if (c.tables.length > 0) cell.tables = c.tables;
2952
- if (c.floats.length > 0) cell.floats = c.floats.map((f) => ({ ...f, y: f.y + dy }));
3433
+ if (c.floats.length > 0)
3434
+ cell.floats = c.floats.map((f) => ({ ...f, y: f.y + dy }));
2953
3435
  return cell;
2954
3436
  });
2955
- const resolved = { x: contentLeft + xShift, y: 0, width: tableWidth, height: rowY[nrows], cells };
3437
+ const resolved = {
3438
+ x: contentLeft + xShift,
3439
+ y: 0,
3440
+ width: tableWidth,
3441
+ height: rowY[nrows],
3442
+ cells
3443
+ };
2956
3444
  if (table.borders) resolved.borders = table.borders;
2957
3445
  let headerRows = 0;
2958
3446
  while (headerRows < nrows && table.rows[headerRows].header) headerRows++;
2959
3447
  if (headerRows > 0 && headerRows < nrows) {
2960
3448
  const headerBottom = rowY[headerRows];
2961
- const spansOut = cells.some((c) => c.y < headerBottom && c.y + c.height > headerBottom);
3449
+ const spansOut = cells.some(
3450
+ (c) => c.y < headerBottom && c.y + c.height > headerBottom
3451
+ );
2962
3452
  if (!spansOut) resolved.headerBottom = headerBottom;
2963
3453
  }
2964
- const cantSplitBands = table.rows.map((row, r) => row.cantSplit ? { top: rowY[r], bottom: rowY[r + 1] } : null).filter((b) => b !== null);
3454
+ const cantSplitBands = table.rows.map(
3455
+ (row, r) => row.cantSplit ? { top: rowY[r], bottom: rowY[r + 1] } : null
3456
+ ).filter((b) => b !== null);
2965
3457
  if (cantSplitBands.length > 0) resolved.cantSplitBands = cantSplitBands;
2966
3458
  return resolved;
2967
3459
  }
@@ -2994,7 +3486,10 @@ function cloneTable(t) {
2994
3486
  return { ...t, cells: t.cells.map(cloneCell) };
2995
3487
  }
2996
3488
  function cloneCell(cell) {
2997
- const copy = { ...cell, lines: cell.lines.map((l) => ({ ...l })) };
3489
+ const copy = {
3490
+ ...cell,
3491
+ lines: cell.lines.map((l) => ({ ...l }))
3492
+ };
2998
3493
  if (cell.tables) copy.tables = cell.tables.map(cloneTable);
2999
3494
  if (cell.floats) copy.floats = cell.floats.map((f) => ({ ...f }));
3000
3495
  return copy;
@@ -3030,11 +3525,19 @@ function splitTableAt(table, cut) {
3030
3525
  } else {
3031
3526
  const c = straddlers.get(cell);
3032
3527
  const topLines = cell.lines.filter((l) => l.y + l.height <= cut);
3033
- const topTables = (cell.tables ?? []).filter((t) => t.y + t.height <= cut);
3034
- const topCell = { ...cell, height: cut - cell.y, lines: topLines };
3528
+ const topTables = (cell.tables ?? []).filter(
3529
+ (t) => t.y + t.height <= cut
3530
+ );
3531
+ const topCell = {
3532
+ ...cell,
3533
+ height: cut - cell.y,
3534
+ lines: topLines
3535
+ };
3035
3536
  if (topTables.length > 0) topCell.tables = topTables;
3036
3537
  else delete topCell.tables;
3037
- const topFloats = (cell.floats ?? []).filter((f) => f.y + f.height <= cut);
3538
+ const topFloats = (cell.floats ?? []).filter(
3539
+ (f) => f.y + f.height <= cut
3540
+ );
3038
3541
  if (topFloats.length > 0) topCell.floats = topFloats;
3039
3542
  else delete topCell.floats;
3040
3543
  topCells.push(topCell);
@@ -3057,7 +3560,13 @@ function splitTableAt(table, cut) {
3057
3560
  restCells.push(contCell);
3058
3561
  }
3059
3562
  }
3060
- const top = { x: table.x, y: 0, width: table.width, height: cut, cells: topCells };
3563
+ const top = {
3564
+ x: table.x,
3565
+ y: 0,
3566
+ width: table.width,
3567
+ height: cut,
3568
+ cells: topCells
3569
+ };
3061
3570
  const rest = {
3062
3571
  x: table.x,
3063
3572
  y: 0,
@@ -3079,10 +3588,14 @@ function cloneHeaderCells(table, headerBottom) {
3079
3588
  tables: void 0,
3080
3589
  floats: cell.floats?.map((f) => ({ ...f })),
3081
3590
  lines: cell.lines.map((l) => {
3082
- const line = { ...l, segments: l.segments.map((s) => ({ ...s, pos: void 0 })) };
3591
+ const line = {
3592
+ ...l,
3593
+ segments: l.segments.map((s) => ({ ...s, pos: void 0 }))
3594
+ };
3083
3595
  delete line.from;
3084
3596
  delete line.to;
3085
- if (line.images) line.images = line.images.map((im) => ({ ...im, pos: void 0 }));
3597
+ if (line.images)
3598
+ line.images = line.images.map((im) => ({ ...im, pos: void 0 }));
3086
3599
  return line;
3087
3600
  })
3088
3601
  }));
@@ -3159,13 +3672,15 @@ function placeBlocks(items, config, ctx, band, footnotes) {
3159
3672
  for (const l of ls)
3160
3673
  for (const s of l.segments) {
3161
3674
  const n = s.footnoteRef;
3162
- if (n != null && footnotes?.has(n) && !pageFnSet.has(n) && !out.includes(n)) out.push(n);
3675
+ if (n != null && footnotes?.has(n) && !pageFnSet.has(n) && !out.includes(n))
3676
+ out.push(n);
3163
3677
  }
3164
3678
  };
3165
3679
  for (const c of t.cells) {
3166
3680
  if (c.y >= maxY) continue;
3167
3681
  scan(c.lines);
3168
- if (c.tables) for (const nt of c.tables) for (const nc of nt.cells) scan(nc.lines);
3682
+ if (c.tables)
3683
+ for (const nt of c.tables) for (const nc of nt.cells) scan(nc.lines);
3169
3684
  }
3170
3685
  return out;
3171
3686
  };
@@ -3194,10 +3709,16 @@ function placeBlocks(items, config, ctx, band, footnotes) {
3194
3709
  return { separatorY: areaTop + FOOTNOTE_AREA_GAP / 2, lines: out };
3195
3710
  };
3196
3711
  const finalizePage = () => {
3197
- const resolved = { index: pages.length, width: page.width, height: page.height, lines };
3712
+ const resolved = {
3713
+ index: pages.length,
3714
+ width: page.width,
3715
+ height: page.height,
3716
+ lines
3717
+ };
3198
3718
  if (tables.length > 0) resolved.tables = tables;
3199
3719
  if (pageFloats.length > 0) resolved.floats = pageFloats;
3200
- if (pageFnNums.length > 0) resolved.footnotes = buildFootnoteArea(pageFnNums);
3720
+ if (pageFnNums.length > 0)
3721
+ resolved.footnotes = buildFootnoteArea(pageFnNums);
3201
3722
  pages.push(resolved);
3202
3723
  lines = [];
3203
3724
  tables = [];
@@ -3287,7 +3808,9 @@ function placeBlocks(items, config, ctx, band, footnotes) {
3287
3808
  }
3288
3809
  const b = bandAt(y, estH);
3289
3810
  if (b) return b;
3290
- const blockers = exclusions.filter((ex) => ex.top < y + estH && ex.bottom > y);
3811
+ const blockers = exclusions.filter(
3812
+ (ex) => ex.top < y + estH && ex.bottom > y
3813
+ );
3291
3814
  if (blockers.length === 0) return { left: colX0(), right: colX1() };
3292
3815
  y = Math.min(...blockers.map((ex) => ex.bottom));
3293
3816
  }
@@ -3331,7 +3854,9 @@ function placeBlocks(items, config, ctx, band, footnotes) {
3331
3854
  }
3332
3855
  const drafts = item.para.drafts;
3333
3856
  const draftsHeight = drafts?.reduce((s, d) => s + d.height, 0) ?? 0;
3334
- const floatsAhead = exclusions.some((ex) => ex.bottom > y && ex.top < y + draftsHeight);
3857
+ const floatsAhead = exclusions.some(
3858
+ (ex) => ex.bottom > y && ex.top < y + draftsHeight
3859
+ );
3335
3860
  if (drafts && !floatsAhead) {
3336
3861
  for (const d of drafts) emitLine(d);
3337
3862
  } else {
@@ -3427,16 +3952,25 @@ function shiftDrafts(drafts, delta) {
3427
3952
  ...d,
3428
3953
  from: d.from != null ? d.from + delta : d.from,
3429
3954
  to: d.to != null ? d.to + delta : d.to,
3430
- segments: d.segments.map((s) => s.pos != null ? { ...s, pos: s.pos + delta } : s),
3431
- images: d.images.map((im) => im.pos != null ? { ...im, pos: im.pos + delta } : im)
3955
+ segments: d.segments.map(
3956
+ (s) => s.pos != null ? { ...s, pos: s.pos + delta } : s
3957
+ ),
3958
+ images: d.images.map(
3959
+ (im) => im.pos != null ? { ...im, pos: im.pos + delta } : im
3960
+ )
3432
3961
  }));
3433
3962
  }
3434
3963
  function cloneLineShifted(l, delta) {
3435
3964
  const out = {
3436
3965
  ...l,
3437
- segments: l.segments.map((s) => s.pos != null ? { ...s, pos: s.pos + delta } : { ...s })
3966
+ segments: l.segments.map(
3967
+ (s) => s.pos != null ? { ...s, pos: s.pos + delta } : { ...s }
3968
+ )
3438
3969
  };
3439
- if (l.images) out.images = l.images.map((im) => im.pos != null ? { ...im, pos: im.pos + delta } : { ...im });
3970
+ if (l.images)
3971
+ out.images = l.images.map(
3972
+ (im) => im.pos != null ? { ...im, pos: im.pos + delta } : { ...im }
3973
+ );
3440
3974
  if (l.from != null) out.from = l.from + delta;
3441
3975
  if (l.to != null) out.to = l.to + delta;
3442
3976
  return out;
@@ -3479,7 +4013,10 @@ function layVariants(docs, fn) {
3479
4013
  return out;
3480
4014
  }
3481
4015
  function maxBandHeight(bands) {
3482
- return Math.max(0, ...[bands.default, bands.first, bands.even].filter(Boolean).map((b) => b.height));
4016
+ return Math.max(
4017
+ 0,
4018
+ ...[bands.default, bands.first, bands.even].filter(Boolean).map((b) => b.height)
4019
+ );
3483
4020
  }
3484
4021
  function anyBandHasFields(bands) {
3485
4022
  return [bands.default, bands.first, bands.even].some(
@@ -3505,9 +4042,17 @@ function layoutChrome(doc, topY, left, right, ctx) {
3505
4042
  const lines = flow.lines.map((l) => ({ ...l, y: l.y + topY }));
3506
4043
  flow.tables.forEach((t) => offsetTable(t, topY));
3507
4044
  stripPositions(lines, flow.tables);
3508
- const band = { lines, tables: flow.tables, height: flow.height };
4045
+ const band = {
4046
+ lines,
4047
+ tables: flow.tables,
4048
+ height: flow.height
4049
+ };
3509
4050
  if (flow.floats.length > 0)
3510
- band.floats = flow.floats.map((f) => ({ ...f, y: f.y + topY, pos: void 0 }));
4051
+ band.floats = flow.floats.map((f) => ({
4052
+ ...f,
4053
+ y: f.y + topY,
4054
+ pos: void 0
4055
+ }));
3511
4056
  return band;
3512
4057
  }
3513
4058
  function layoutFooterChrome(doc, pageHeight, left, right, ctx) {
@@ -3519,12 +4064,21 @@ function layoutFooterChrome(doc, pageHeight, left, right, ctx) {
3519
4064
  height: flow.height
3520
4065
  };
3521
4066
  band.tables.forEach((t) => offsetTable(t, topY));
3522
- if (flow.floats) band.floats = flow.floats.map((f) => ({ ...f, y: f.y + topY }));
4067
+ if (flow.floats)
4068
+ band.floats = flow.floats.map((f) => ({ ...f, y: f.y + topY }));
3523
4069
  return band;
3524
4070
  }
3525
4071
  function layoutFootnoteBody(doc, left, right, ctx) {
3526
- const fnCtx = { ...ctx, base: { ...ctx.base, sizePt: ctx.base.sizePt * FOOTNOTE_FONT_SCALE } };
3527
- const flow = layoutFlow(toFlowBlocks(doc, fnCtx.base, false), left, right, fnCtx);
4072
+ const fnCtx = {
4073
+ ...ctx,
4074
+ base: { ...ctx.base, sizePt: ctx.base.sizePt * FOOTNOTE_FONT_SCALE }
4075
+ };
4076
+ const flow = layoutFlow(
4077
+ toFlowBlocks(doc, fnCtx.base, false),
4078
+ left,
4079
+ right,
4080
+ fnCtx
4081
+ );
3528
4082
  stripPositions(flow.lines, flow.tables);
3529
4083
  return { lines: flow.lines, height: flow.height };
3530
4084
  }
@@ -3533,10 +4087,24 @@ function layout(doc, config, cache, chrome, footnotes) {
3533
4087
  const { page } = config;
3534
4088
  const left = page.margin.left;
3535
4089
  const right = page.width - page.margin.right;
3536
- const headerDocs = { default: chrome?.header, first: chrome?.headerFirst, even: chrome?.headerEven };
3537
- const footerDocs = { default: chrome?.footer, first: chrome?.footerFirst, even: chrome?.footerEven };
3538
- const layHeaders = (c) => layVariants(headerDocs, (d) => layoutChrome(d, CHROME_DISTANCE, left, right, c));
3539
- const layFooters = (c) => layVariants(footerDocs, (d) => layoutFooterChrome(d, page.height, left, right, c));
4090
+ const headerDocs = {
4091
+ default: chrome?.header,
4092
+ first: chrome?.headerFirst,
4093
+ even: chrome?.headerEven
4094
+ };
4095
+ const footerDocs = {
4096
+ default: chrome?.footer,
4097
+ first: chrome?.footerFirst,
4098
+ even: chrome?.footerEven
4099
+ };
4100
+ const layHeaders = (c) => layVariants(
4101
+ headerDocs,
4102
+ (d) => layoutChrome(d, CHROME_DISTANCE, left, right, c)
4103
+ );
4104
+ const layFooters = (c) => layVariants(
4105
+ footerDocs,
4106
+ (d) => layoutFooterChrome(d, page.height, left, right, c)
4107
+ );
3540
4108
  let headers = layHeaders(ctx);
3541
4109
  let footers = layFooters(ctx);
3542
4110
  let top = page.margin.top;
@@ -3545,25 +4113,46 @@ function layout(doc, config, cache, chrome, footnotes) {
3545
4113
  top = Math.max(top, CHROME_DISTANCE + maxBandHeight(headers));
3546
4114
  }
3547
4115
  if (footers.default || footers.first || footers.even) {
3548
- bottom = Math.min(bottom, page.height - CHROME_DISTANCE - maxBandHeight(footers));
4116
+ bottom = Math.min(
4117
+ bottom,
4118
+ page.height - CHROME_DISTANCE - maxBandHeight(footers)
4119
+ );
3549
4120
  }
3550
- const counter = createNumberingCounter(doc.attrs["numbering"]);
4121
+ const counter = createNumberingCounter(
4122
+ doc.attrs["numbering"]
4123
+ );
3551
4124
  const sections = doc.attrs["sections"] ?? [
3552
- { blockCount: doc.childCount, columns: { count: 1, gap: 0 }, newPage: true }
4125
+ {
4126
+ blockCount: doc.childCount,
4127
+ columns: { count: 1, gap: 0 },
4128
+ newPage: true
4129
+ }
3553
4130
  ];
3554
4131
  const blockSection = [];
3555
4132
  for (const sec of sections) {
3556
4133
  for (let k = 0; k < sec.blockCount; k++) {
3557
- blockSection.push({ columns: sec.columns, start: k === 0, newPage: sec.newPage });
4134
+ blockSection.push({
4135
+ columns: sec.columns,
4136
+ start: k === 0,
4137
+ newPage: sec.newPage
4138
+ });
3558
4139
  }
3559
4140
  }
3560
4141
  const lastSec = sections[sections.length - 1];
3561
4142
  while (blockSection.length < doc.childCount) {
3562
- blockSection.push({ columns: lastSec.columns, start: false, newPage: lastSec.newPage });
4143
+ blockSection.push({
4144
+ columns: lastSec.columns,
4145
+ start: false,
4146
+ newPage: lastSec.newPage
4147
+ });
3563
4148
  }
3564
4149
  const items = [];
3565
4150
  doc.forEach((node, offset, index) => {
3566
- const bs = blockSection[index] ?? { columns: { count: 1, gap: 0 }, start: false, newPage: true };
4151
+ const bs = blockSection[index] ?? {
4152
+ columns: { count: 1, gap: 0 },
4153
+ start: false,
4154
+ newPage: true
4155
+ };
3567
4156
  const colRight = left + columnWidth(right - left, bs.columns);
3568
4157
  const tag = (item) => {
3569
4158
  if (bs.start) item.section = { ...bs.columns, newPage: bs.newPage };
@@ -3596,17 +4185,35 @@ function layout(doc, config, cache, chrome, footnotes) {
3596
4185
  }
3597
4186
  const flow = paragraphToFlow(node, ctx.base, offset, true, marker);
3598
4187
  const drafts = layoutParagraph(flow, left, colRight, ctx);
3599
- cache?.paragraphs.set(node, { left, right: colRight, basePos: contentStart, marker, drafts });
4188
+ cache?.paragraphs.set(node, {
4189
+ left,
4190
+ right: colRight,
4191
+ basePos: contentStart,
4192
+ marker,
4193
+ drafts
4194
+ });
3600
4195
  items.push(tag({ para: { ...para(drafts), getFlow: () => flow } }));
3601
4196
  } else if (node.type.name === "table") {
3602
4197
  const hit = cache?.tables.get(node);
3603
4198
  if (hit && hit.left === left && hit.right === colRight) {
3604
- items.push(tag({ table: cloneTableShifted(hit.table, offset - hit.basePos) }));
4199
+ items.push(
4200
+ tag({ table: cloneTableShifted(hit.table, offset - hit.basePos) })
4201
+ );
3605
4202
  return;
3606
4203
  }
3607
- const table = layoutTable(tableToFlow(node, ctx.base, offset, counter), left, colRight, ctx);
4204
+ const table = layoutTable(
4205
+ tableToFlow(node, ctx.base, offset, counter),
4206
+ left,
4207
+ colRight,
4208
+ ctx
4209
+ );
3608
4210
  if (cache && !tableHasList(node)) {
3609
- cache.tables.set(node, { left, right: colRight, basePos: offset, table });
4211
+ cache.tables.set(node, {
4212
+ left,
4213
+ right: colRight,
4214
+ basePos: offset,
4215
+ table
4216
+ });
3610
4217
  items.push(tag({ table: cloneTableShifted(table, 0) }));
3611
4218
  } else {
3612
4219
  items.push(tag({ table }));
@@ -3624,7 +4231,10 @@ function layout(doc, config, cache, chrome, footnotes) {
3624
4231
  assignSectionHeights(items);
3625
4232
  const resolved = placeBlocks(items, config, ctx, { top, bottom }, fnMap);
3626
4233
  if (anyBandHasFields(headers) || anyBandHasFields(footers)) {
3627
- const fieldCtx = { ...ctx, fieldPlaceholder: String(resolved.pages.length) };
4234
+ const fieldCtx = {
4235
+ ...ctx,
4236
+ fieldPlaceholder: String(resolved.pages.length)
4237
+ };
3628
4238
  headers = layHeaders(fieldCtx);
3629
4239
  footers = layFooters(fieldCtx);
3630
4240
  }
@@ -3635,7 +4245,10 @@ function layout(doc, config, cache, chrome, footnotes) {
3635
4245
  if (footers.first) resolved.pageFooterFirst = footers.first;
3636
4246
  if (footers.even) resolved.pageFooterEven = footers.even;
3637
4247
  if (chrome?.titlePg || chrome?.evenAndOdd) {
3638
- resolved.chromeSelect = { titlePg: !!chrome.titlePg, evenAndOdd: !!chrome.evenAndOdd };
4248
+ resolved.chromeSelect = {
4249
+ titlePg: !!chrome.titlePg,
4250
+ evenAndOdd: !!chrome.evenAndOdd
4251
+ };
3639
4252
  }
3640
4253
  return resolved;
3641
4254
  }
@@ -4873,7 +5486,10 @@ var splitListItem = (state, dispatch) => {
4873
5486
  if ($from.parent !== $to.parent) return false;
4874
5487
  if (parent.content.size === 0) {
4875
5488
  dispatch?.(
4876
- state.tr.setNodeMarkup($from.before(), null, { ...parent.attrs, list: null })
5489
+ state.tr.setNodeMarkup($from.before(), null, {
5490
+ ...parent.attrs,
5491
+ list: null
5492
+ })
4877
5493
  );
4878
5494
  return true;
4879
5495
  }
@@ -4949,6 +5565,7 @@ var InputBridge = class {
4949
5565
  this.dom.appendChild(this.host);
4950
5566
  this.view = new import_prosemirror_view.EditorView(this.host, {
4951
5567
  state: createEditingState(options.doc, options.keys),
5568
+ handlePaste: options.handlePaste,
4952
5569
  dispatchTransaction: (tr) => {
4953
5570
  const state = this.view.state.apply(tr);
4954
5571
  this.view.updateState(state);
@@ -4972,7 +5589,10 @@ var InputBridge = class {
4972
5589
  setSelection(anchor, head = anchor) {
4973
5590
  const { doc } = this.view.state;
4974
5591
  const clamp2 = (p) => Math.max(0, Math.min(p, doc.content.size));
4975
- const sel = import_prosemirror_state.TextSelection.between(doc.resolve(clamp2(anchor)), doc.resolve(clamp2(head)));
5592
+ const sel = import_prosemirror_state.TextSelection.between(
5593
+ doc.resolve(clamp2(anchor)),
5594
+ doc.resolve(clamp2(head))
5595
+ );
4976
5596
  this.view.dispatch(this.view.state.tr.setSelection(sel));
4977
5597
  }
4978
5598
  /** Select the word around `pos` (double-click); falls back to a collapsed
@@ -5787,7 +6407,8 @@ function setAlign(align) {
5787
6407
  const tr = state.tr;
5788
6408
  let changed = false;
5789
6409
  state.doc.nodesBetween(from, to, (node, pos) => {
5790
- if (node.type.name !== "paragraph" || node.attrs["align"] === align) return;
6410
+ if (node.type.name !== "paragraph" || node.attrs["align"] === align)
6411
+ return;
5791
6412
  tr.setNodeAttribute(pos, "align", align);
5792
6413
  changed = true;
5793
6414
  });
@@ -5812,7 +6433,9 @@ function toggleHeading(level) {
5812
6433
  const paras = selectedParagraphs(state);
5813
6434
  if (paras.length === 0) return false;
5814
6435
  if (dispatch) {
5815
- const allThisLevel = paras.every((p) => p.node.attrs["heading"] === level);
6436
+ const allThisLevel = paras.every(
6437
+ (p) => p.node.attrs["heading"] === level
6438
+ );
5816
6439
  const next = allThisLevel ? null : level;
5817
6440
  const tr = state.tr;
5818
6441
  for (const p of paras) tr.setNodeAttribute(p.pos, "heading", next);
@@ -6075,8 +6698,10 @@ var BapbongEditor = class {
6075
6698
  * Built from the shared command layer; the same ops run on a backend.
6076
6699
  * (Plugin-contributed commands are an additive follow-up.) */
6077
6700
  commands = defaultCommands();
6701
+ readClipboardFallback;
6078
6702
  constructor(stack, opts = {}) {
6079
6703
  this.stack = stack;
6704
+ this.readClipboardFallback = opts.readClipboardFallback;
6080
6705
  this.core = new RenderCore(stack, {
6081
6706
  viewport: opts.viewport,
6082
6707
  measureText: opts.measureText,
@@ -6093,9 +6718,12 @@ var BapbongEditor = class {
6093
6718
  stack.addEventListener("dblclick", this.onDblClick);
6094
6719
  stack.addEventListener("contextmenu", this.onContextMenu);
6095
6720
  window.addEventListener("keydown", this.onKeyDown, true);
6096
- this.plugins = new Collection([...createBuiltins(), ...opts.plugins ?? []], {
6097
- idProperty: "name"
6098
- });
6721
+ this.plugins = new Collection(
6722
+ [...createBuiltins(), ...opts.plugins ?? []],
6723
+ {
6724
+ idProperty: "name"
6725
+ }
6726
+ );
6099
6727
  this.pointerPlugins = [...this.plugins].some((p) => p.onPointer);
6100
6728
  this.pluginCtx = this.makePluginContext();
6101
6729
  for (const p of this.plugins) {
@@ -6120,8 +6748,14 @@ var BapbongEditor = class {
6120
6748
  setActionButton: (at, onActivate) => this.setActionButton(at, onActivate),
6121
6749
  setFrame: (frame) => this.setFrame(frame)
6122
6750
  };
6123
- Object.defineProperty(ctx, "state", { enumerable: true, get: () => this.state });
6124
- Object.defineProperty(ctx, "layout", { enumerable: true, get: () => this.core.layout });
6751
+ Object.defineProperty(ctx, "state", {
6752
+ enumerable: true,
6753
+ get: () => this.state
6754
+ });
6755
+ Object.defineProperty(ctx, "layout", {
6756
+ enumerable: true,
6757
+ get: () => this.core.layout
6758
+ });
6125
6759
  return ctx;
6126
6760
  }
6127
6761
  // ── Public API ──────────────────────────────────────────────────────
@@ -6264,34 +6898,116 @@ var BapbongEditor = class {
6264
6898
  this.bridge.focus();
6265
6899
  return document.execCommand("cut");
6266
6900
  }
6267
- /** Paste clipboard content (HTML parsed by the schema, else plain text). */
6901
+ /** Paste clipboard content HTML parsed by the schema, image blobs as
6902
+ * embedded images, else plain text.
6903
+ *
6904
+ * Menu-driven paste can't ride a native ClipboardEvent, so it climbs a
6905
+ * ladder of acquisition strategies until one yields content:
6906
+ * 1. the host's native clipboard reader (`readClipboardFallback`) when
6907
+ * provided — on WKWebView every programmatic clipboard READ (both
6908
+ * `navigator.clipboard.*` and execCommand) pops a "Paste" permission
6909
+ * callout and pends until the user taps it, so the desktop shell
6910
+ * must never touch the DOM clipboard from a menu click;
6911
+ * 2. async Clipboard API (`navigator.clipboard.read`) — browsers, where
6912
+ * a user-gesture read is granted silently;
6913
+ * 3. `document.execCommand('paste')` — legacy last resort. */
6268
6914
  async paste() {
6269
6915
  const view = this.bridge?.view;
6270
6916
  if (!view) return;
6917
+ this.bridge?.focus();
6918
+ if (await this.pasteViaHostClipboard(view)) return;
6919
+ if (this.readClipboardFallback) return;
6920
+ if (await this.pasteViaClipboardApi(view)) return;
6921
+ document.execCommand("paste");
6922
+ }
6923
+ async pasteViaClipboardApi(view) {
6271
6924
  try {
6272
6925
  const items = await navigator.clipboard.read();
6926
+ const htmlItem = items.find((i) => i.types.includes("text/html"));
6927
+ if (htmlItem) {
6928
+ const html = await (await htmlItem.getType("text/html")).text();
6929
+ const dom = new DOMParser().parseFromString(html, "text/html").body;
6930
+ if ((dom.textContent ?? "").trim()) {
6931
+ const slice = import_prosemirror_model3.DOMParser.fromSchema(view.state.schema).parseSlice(
6932
+ dom
6933
+ );
6934
+ view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
6935
+ return true;
6936
+ }
6937
+ }
6273
6938
  for (const item of items) {
6274
- if (item.types.includes("text/html")) {
6275
- const html = await (await item.getType("text/html")).text();
6276
- const dom = document.createElement("div");
6277
- dom.innerHTML = html;
6278
- const slice = import_prosemirror_model3.DOMParser.fromSchema(view.state.schema).parseSlice(dom);
6939
+ const type = item.types.find((t) => t.startsWith("image/"));
6940
+ if (type && await insertImageBlobs(view, [await item.getType(type)]))
6941
+ return true;
6942
+ }
6943
+ const text = await navigator.clipboard.readText();
6944
+ if (text) {
6945
+ view.dispatch(view.state.tr.insertText(text).scrollIntoView());
6946
+ return true;
6947
+ }
6948
+ return false;
6949
+ } catch (err) {
6950
+ console.warn(
6951
+ "[bapbong] Clipboard API paste unavailable, falling back:",
6952
+ err
6953
+ );
6954
+ return false;
6955
+ }
6956
+ }
6957
+ async pasteViaHostClipboard(view) {
6958
+ if (!this.readClipboardFallback) return false;
6959
+ try {
6960
+ const data = await this.readClipboardFallback();
6961
+ if (!data) return false;
6962
+ if (data.html) {
6963
+ const dom = new DOMParser().parseFromString(
6964
+ data.html,
6965
+ "text/html"
6966
+ ).body;
6967
+ if ((dom.textContent ?? "").trim()) {
6968
+ const slice = import_prosemirror_model3.DOMParser.fromSchema(view.state.schema).parseSlice(
6969
+ dom
6970
+ );
6279
6971
  view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
6280
- return;
6972
+ return true;
6281
6973
  }
6282
6974
  }
6283
- await this.pasteText();
6284
- } catch {
6975
+ if (data.image?.length) {
6976
+ const blob = new Blob([data.image], {
6977
+ type: data.imageMime ?? "image/png"
6978
+ });
6979
+ if (await insertImageBlobs(view, [blob])) return true;
6980
+ }
6981
+ if (data.text) {
6982
+ view.dispatch(view.state.tr.insertText(data.text).scrollIntoView());
6983
+ return true;
6984
+ }
6985
+ return false;
6986
+ } catch (err) {
6987
+ console.warn("[bapbong] host clipboard fallback failed:", err);
6988
+ return false;
6285
6989
  }
6286
6990
  }
6287
6991
  /** Paste clipboard text as plain text (no formatting). */
6288
6992
  async pasteText() {
6289
6993
  const view = this.bridge?.view;
6290
6994
  if (!view) return;
6995
+ this.bridge?.focus();
6996
+ if (this.readClipboardFallback) {
6997
+ try {
6998
+ const data = await this.readClipboardFallback();
6999
+ if (data?.text)
7000
+ view.dispatch(view.state.tr.insertText(data.text).scrollIntoView());
7001
+ } catch (err) {
7002
+ console.warn("[bapbong] host clipboard fallback failed:", err);
7003
+ }
7004
+ return;
7005
+ }
6291
7006
  try {
6292
7007
  const text = await navigator.clipboard.readText();
6293
7008
  if (text) view.dispatch(view.state.tr.insertText(text).scrollIntoView());
6294
- } catch {
7009
+ } catch (err) {
7010
+ console.warn("[bapbong] Clipboard API readText unavailable:", err);
6295
7011
  }
6296
7012
  }
6297
7013
  destroy() {
@@ -6332,7 +7048,8 @@ var BapbongEditor = class {
6332
7048
  "Shift-ArrowUp": this.verticalCmd(-1, true),
6333
7049
  "Shift-ArrowDown": this.verticalCmd(1, true)
6334
7050
  },
6335
- onUpdate: (state, tr) => this.refresh(state, tr)
7051
+ onUpdate: (state, tr) => this.refresh(state, tr),
7052
+ handlePaste: imagePasteHandler
6336
7053
  });
6337
7054
  this.stack.appendChild(this.bridge.dom);
6338
7055
  this.render(this.bridge.state, true);
@@ -6361,14 +7078,25 @@ var BapbongEditor = class {
6361
7078
  else this.core.paintOverlay(overlay);
6362
7079
  const caret = this.lastCaret;
6363
7080
  if (caret && this.bridge) {
6364
- const pt = this.core.pageToCanvas({ pageIndex: caret.pageIndex, x: caret.x, y: caret.y });
7081
+ const pt = this.core.pageToCanvas({
7082
+ pageIndex: caret.pageIndex,
7083
+ x: caret.x,
7084
+ y: caret.y
7085
+ });
6365
7086
  if (pt) this.bridge.place(pt.x, pt.y, caret.height);
6366
7087
  }
6367
- this.emitChange({ state, pageCount: this.core.pageCount, docChanged: contentDirty });
7088
+ this.emitChange({
7089
+ state,
7090
+ pageCount: this.core.pageCount,
7091
+ docChanged: contentDirty
7092
+ });
6368
7093
  }
6369
7094
  /** The caret/selection overlay in the current blink phase. */
6370
7095
  currentOverlay() {
6371
- return { caret: this.caretVisible ? this.lastCaret : null, selection: this.lastSelection };
7096
+ return {
7097
+ caret: this.caretVisible ? this.lastCaret : null,
7098
+ selection: this.lastSelection
7099
+ };
6372
7100
  }
6373
7101
  /** Each plugin's doc-range decorations (the core resolves them to rects). */
6374
7102
  pluginDecorations() {
@@ -6443,7 +7171,8 @@ var BapbongEditor = class {
6443
7171
  * gesture), so plugins never steal keys from other inputs on the page. */
6444
7172
  onKeyDown = (ev) => {
6445
7173
  const target = ev.target;
6446
- if (target && target !== document.body && !this.stack.contains(target)) return;
7174
+ if (target && target !== document.body && !this.stack.contains(target))
7175
+ return;
6447
7176
  const offered = {
6448
7177
  key: ev.key,
6449
7178
  ctrlKey: ev.ctrlKey,
@@ -6477,7 +7206,10 @@ var BapbongEditor = class {
6477
7206
  this.lastCaret = this.core.caretRect(this.dragHead);
6478
7207
  this.lastSelection = from === to ? [] : this.core.selectionRects(from, to);
6479
7208
  this.caretVisible = true;
6480
- this.core.paintOverlay({ caret: this.lastCaret, selection: this.lastSelection });
7209
+ this.core.paintOverlay({
7210
+ caret: this.lastCaret,
7211
+ selection: this.lastSelection
7212
+ });
6481
7213
  }
6482
7214
  onPointerUp = (ev) => {
6483
7215
  if (this.offerPointer("up", ev)) {
@@ -6538,8 +7270,16 @@ var BapbongEditor = class {
6538
7270
  this.guideEl.style.cssText = "position:absolute;width:0;border-left:2px dashed #378add;pointer-events:none;z-index:5;";
6539
7271
  this.stack.appendChild(this.guideEl);
6540
7272
  }
6541
- const top = this.core.pageToCanvas({ pageIndex: guide.pageIndex, x: guide.x, y: guide.y });
6542
- const bottom = this.core.pageToCanvas({ pageIndex: guide.pageIndex, x: guide.x, y: guide.y + guide.height });
7273
+ const top = this.core.pageToCanvas({
7274
+ pageIndex: guide.pageIndex,
7275
+ x: guide.x,
7276
+ y: guide.y
7277
+ });
7278
+ const bottom = this.core.pageToCanvas({
7279
+ pageIndex: guide.pageIndex,
7280
+ x: guide.x,
7281
+ y: guide.y + guide.height
7282
+ });
6543
7283
  if (!top || !bottom) {
6544
7284
  this.guideEl.style.display = "none";
6545
7285
  return;
@@ -6587,7 +7327,11 @@ var BapbongEditor = class {
6587
7327
  this.stack.appendChild(el2);
6588
7328
  this.frameEl = el2;
6589
7329
  }
6590
- const tl = this.core.pageToCanvas({ pageIndex: frame.pageIndex, x: frame.x, y: frame.y });
7330
+ const tl = this.core.pageToCanvas({
7331
+ pageIndex: frame.pageIndex,
7332
+ x: frame.x,
7333
+ y: frame.y
7334
+ });
6591
7335
  const br = this.core.pageToCanvas({
6592
7336
  pageIndex: frame.pageIndex,
6593
7337
  x: frame.x + frame.width,
@@ -6623,7 +7367,11 @@ var BapbongEditor = class {
6623
7367
  this.highlightEls.forEach((el, i) => {
6624
7368
  const r = list[i];
6625
7369
  const tl = r && this.core.pageToCanvas({ pageIndex: r.pageIndex, x: r.x, y: r.y });
6626
- const br = r && this.core.pageToCanvas({ pageIndex: r.pageIndex, x: r.x + r.width, y: r.y + r.height });
7370
+ const br = r && this.core.pageToCanvas({
7371
+ pageIndex: r.pageIndex,
7372
+ x: r.x + r.width,
7373
+ y: r.y + r.height
7374
+ });
6627
7375
  if (!tl || !br) {
6628
7376
  el.style.display = "none";
6629
7377
  return;