@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.js CHANGED
@@ -1,8 +1,98 @@
1
1
  // packages/editor/src/lib/bapbong-editor.ts
2
- import { DOMParser as PMDOMParser, Schema as Schema2 } from "prosemirror-model";
2
+ import {
3
+ DOMParser as PMDOMParser,
4
+ Schema as Schema2
5
+ } from "prosemirror-model";
6
+
7
+ // packages/editor/src/lib/paste-images.ts
8
+ var MAX_DISPLAY_WIDTH = 600;
9
+ function imageFilesIn(dt) {
10
+ const files = [];
11
+ for (const item of Array.from(dt?.items ?? [])) {
12
+ if (item.kind === "file" && item.type.startsWith("image/")) {
13
+ const f = item.getAsFile();
14
+ if (f) files.push(f);
15
+ }
16
+ }
17
+ return files;
18
+ }
19
+ async function bitmapSize(blob) {
20
+ try {
21
+ const bmp = await createImageBitmap(blob);
22
+ const size = { width: bmp.width, height: bmp.height };
23
+ bmp.close();
24
+ return size;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+ var toDataURL = (blob) => new Promise((resolve, reject) => {
30
+ const r = new FileReader();
31
+ r.onload = () => resolve(r.result);
32
+ r.onerror = () => reject(r.error);
33
+ r.readAsDataURL(blob);
34
+ });
35
+ async function insertImageBlobs(view, blobs) {
36
+ const imageType = view.state.schema.nodes["image"];
37
+ if (!imageType) return false;
38
+ const nodes = [];
39
+ for (const blob of blobs) {
40
+ const size = await bitmapSize(blob);
41
+ if (!size) continue;
42
+ const scale = Math.min(1, MAX_DISPLAY_WIDTH / size.width);
43
+ nodes.push(
44
+ imageType.create({
45
+ src: await toDataURL(blob),
46
+ width: Math.round(size.width * scale),
47
+ height: Math.round(size.height * scale)
48
+ })
49
+ );
50
+ }
51
+ if (!nodes.length) return false;
52
+ let tr = view.state.tr;
53
+ for (const node of nodes) tr = tr.replaceSelectionWith(node);
54
+ view.dispatch(tr.scrollIntoView());
55
+ return true;
56
+ }
57
+ function imagePasteHandler(view, event) {
58
+ const dt = event.clipboardData;
59
+ const files = imageFilesIn(dt);
60
+ if (!files.length) return false;
61
+ const html = dt?.getData("text/html") ?? "";
62
+ if (html) {
63
+ const body = new DOMParser().parseFromString(html, "text/html").body;
64
+ if ((body.textContent ?? "").trim()) return false;
65
+ }
66
+ void insertImageBlobs(view, files);
67
+ return true;
68
+ }
3
69
 
4
70
  // packages/model/dist/index.js
5
71
  import { Schema } from "prosemirror-model";
72
+ function pastedParagraphAttrs(el, heading) {
73
+ const style = el.getAttribute("style") ?? "";
74
+ const m = /(?:^|;)\s*text-align\s*:\s*(center|right|justify)/i.exec(style);
75
+ return { heading, align: m ? m[1].toLowerCase() : null };
76
+ }
77
+ function pastedImageAttrs(el) {
78
+ const e = el;
79
+ const src = e.getAttribute("src") ?? "";
80
+ if (!/^data:image\//i.test(src)) return false;
81
+ const dim = (v) => {
82
+ const n = parseFloat(v ?? "");
83
+ return Number.isFinite(n) && n > 0 ? Math.round(n) : null;
84
+ };
85
+ return {
86
+ src,
87
+ alt: e.getAttribute("alt") ?? "",
88
+ width: dim(e.getAttribute("width")),
89
+ height: dim(e.getAttribute("height"))
90
+ };
91
+ }
92
+ function pastedLinkAttrs(el) {
93
+ const href = (el.getAttribute("href") ?? "").trim();
94
+ return /^(https?:|mailto:|#)/i.test(href) ? { href } : false;
95
+ }
6
96
  var schema = new Schema({
7
97
  nodes: {
8
98
  doc: {
@@ -38,6 +128,11 @@ var schema = new Schema({
38
128
  // an <h1>–<h6> in toDOM so the a11y mirror is semantic), or null for a
39
129
  // body paragraph.
40
130
  heading: { default: null },
131
+ // Named Word paragraph style with no outline level: 'Title' |
132
+ // 'Subtitle', or null. Mutually exclusive with `heading` — the
133
+ // setParagraphStyle command is the only writer and keeps the
134
+ // invariant (styleId set ⇒ heading null).
135
+ styleId: { default: null },
41
136
  // w:tabs — [{ pos, val: 'left'|'right'|'center'|'decimal', leader? }]
42
137
  // in px from the paragraph's content left edge, or null. Importer-set.
43
138
  tabs: { default: null },
@@ -46,15 +141,23 @@ var schema = new Schema({
46
141
  // w:pageBreakBefore — start this paragraph on a new page.
47
142
  pageBreakBefore: { default: false }
48
143
  },
49
- // No getAttrs: nothing in the pipeline parses paragraphs from the DOM
50
- // yet (the importer builds nodes directly). align/indent still round-trip
51
- // out through toDOM. Revisit when HTML paste lands.
52
- parseDOM: [{ tag: "p" }],
144
+ // HTML paste path: recover heading level from h1–h6 and alignment from
145
+ // inline style. Other attrs (list/indent/tabs/spacing) stay importer-only
146
+ // pasted HTML rarely carries them faithfully.
147
+ parseDOM: [
148
+ { tag: "p", getAttrs: (el) => pastedParagraphAttrs(el, null) },
149
+ ...[1, 2, 3, 4, 5, 6].map((level) => ({
150
+ tag: `h${level}`,
151
+ getAttrs: (el) => pastedParagraphAttrs(el, level)
152
+ }))
153
+ ],
53
154
  toDOM(node) {
54
155
  const attrs = node.attrs;
55
156
  const style = paragraphStyle(attrs);
56
157
  const tag = attrs.heading ? `h${attrs.heading}` : "p";
57
- return [tag, style ? { style } : {}, 0];
158
+ const dom = style ? { style } : {};
159
+ if (attrs.styleId) dom["data-style"] = attrs.styleId;
160
+ return [tag, dom, 0];
58
161
  }
59
162
  },
60
163
  text: { group: "inline" },
@@ -94,9 +197,13 @@ var schema = new Schema({
94
197
  // Paint-only: the layout box stays axis-aligned.
95
198
  rotation: { default: 0 }
96
199
  },
200
+ parseDOM: [{ tag: "img[src]", getAttrs: pastedImageAttrs }],
97
201
  toDOM(node) {
98
202
  const a = node.attrs;
99
- const attrs = { src: a["src"], alt: a["alt"] };
203
+ const attrs = {
204
+ src: a["src"],
205
+ alt: a["alt"]
206
+ };
100
207
  if (a["width"] != null) attrs["width"] = String(a["width"]);
101
208
  if (a["height"] != null) attrs["height"] = String(a["height"]);
102
209
  return ["img", attrs];
@@ -133,7 +240,11 @@ var schema = new Schema({
133
240
  align: { default: null }
134
241
  },
135
242
  parseDOM: [{ tag: "table" }],
136
- toDOM: (node) => ["table", node.attrs["borders"] ? { "data-borders": "1" } : {}, ["tbody", 0]]
243
+ toDOM: (node) => [
244
+ "table",
245
+ node.attrs["borders"] ? { "data-borders": "1" } : {},
246
+ ["tbody", 0]
247
+ ]
137
248
  },
138
249
  table_row: {
139
250
  content: "table_cell+",
@@ -169,9 +280,12 @@ var schema = new Schema({
169
280
  parseDOM: [{ tag: "td" }, { tag: "th" }],
170
281
  toDOM(node) {
171
282
  const attrs = {};
172
- if (node.attrs["colspan"] !== 1) attrs["colspan"] = String(node.attrs["colspan"]);
173
- if (node.attrs["rowspan"] !== 1) attrs["rowspan"] = String(node.attrs["rowspan"]);
174
- if (node.attrs["background"]) attrs["style"] = `background-color: ${node.attrs["background"]}`;
283
+ if (node.attrs["colspan"] !== 1)
284
+ attrs["colspan"] = String(node.attrs["colspan"]);
285
+ if (node.attrs["rowspan"] !== 1)
286
+ attrs["rowspan"] = String(node.attrs["rowspan"]);
287
+ if (node.attrs["background"])
288
+ attrs["style"] = `background-color: ${node.attrs["background"]}`;
175
289
  return ["td", attrs, 0];
176
290
  }
177
291
  }
@@ -200,9 +314,15 @@ var schema = new Schema({
200
314
  // w:color — hex "#RRGGBB"
201
315
  textColor: {
202
316
  attrs: { color: {} },
203
- parseDOM: [{ style: "color", getAttrs: (value) => ({ color: value }) }],
317
+ parseDOM: [
318
+ { style: "color", getAttrs: (value) => ({ color: value }) }
319
+ ],
204
320
  toDOM(mark) {
205
- return ["span", { style: `color: ${mark.attrs["color"]}` }, 0];
321
+ return [
322
+ "span",
323
+ { style: `color: ${mark.attrs["color"]}` },
324
+ 0
325
+ ];
206
326
  }
207
327
  },
208
328
  // w:sz — size in points
@@ -218,7 +338,11 @@ var schema = new Schema({
218
338
  }
219
339
  ],
220
340
  toDOM(mark) {
221
- return ["span", { style: `font-size: ${mark.attrs["size"]}pt` }, 0];
341
+ return [
342
+ "span",
343
+ { style: `font-size: ${mark.attrs["size"]}pt` },
344
+ 0
345
+ ];
222
346
  }
223
347
  },
224
348
  // w:vertAlign — superscript / subscript
@@ -235,26 +359,51 @@ var schema = new Schema({
235
359
  highlight: {
236
360
  attrs: { color: {} },
237
361
  parseDOM: [
238
- { style: "background-color", getAttrs: (value) => ({ color: value }) }
362
+ {
363
+ style: "background-color",
364
+ getAttrs: (value) => ({ color: value })
365
+ }
239
366
  ],
240
367
  toDOM(mark) {
241
- return ["span", { style: `background-color: ${mark.attrs["color"]}` }, 0];
368
+ return [
369
+ "span",
370
+ { style: `background-color: ${mark.attrs["color"]}` },
371
+ 0
372
+ ];
242
373
  }
243
374
  },
244
375
  // w:rFonts — font family
245
376
  fontFamily: {
246
377
  attrs: { family: {} },
247
- parseDOM: [{ style: "font-family", getAttrs: (value) => ({ family: value }) }],
378
+ parseDOM: [
379
+ {
380
+ style: "font-family",
381
+ getAttrs: (value) => ({ family: value })
382
+ }
383
+ ],
248
384
  toDOM(mark) {
249
- return ["span", { style: `font-family: ${mark.attrs["family"]}` }, 0];
385
+ return [
386
+ "span",
387
+ { style: `font-family: ${mark.attrs["family"]}` },
388
+ 0
389
+ ];
250
390
  }
251
391
  },
252
392
  // w:hyperlink — external URL or "#anchor"
253
393
  link: {
254
394
  attrs: { href: {} },
255
395
  inclusive: false,
396
+ parseDOM: [{ tag: "a[href]", getAttrs: pastedLinkAttrs }],
256
397
  toDOM(mark) {
257
- return ["a", { href: mark.attrs["href"], rel: "noopener", target: "_blank" }, 0];
398
+ return [
399
+ "a",
400
+ {
401
+ href: mark.attrs["href"],
402
+ rel: "noopener",
403
+ target: "_blank"
404
+ },
405
+ 0
406
+ ];
258
407
  }
259
408
  },
260
409
  // w:footnoteReference — the carrier text is the superscript number; `num`
@@ -268,12 +417,20 @@ var schema = new Schema({
268
417
  // `el` is an HTMLElement at runtime; this package has no DOM lib, so
269
418
  // narrow structurally rather than naming the type.
270
419
  getAttrs: (el) => ({
271
- num: Number(el.getAttribute("data-footnote")) || 0
420
+ num: Number(
421
+ el.getAttribute(
422
+ "data-footnote"
423
+ )
424
+ ) || 0
272
425
  })
273
426
  }
274
427
  ],
275
428
  toDOM(mark) {
276
- return ["sup", { "data-footnote": String(mark.attrs["num"]) }, 0];
429
+ return [
430
+ "sup",
431
+ { "data-footnote": String(mark.attrs["num"]) },
432
+ 0
433
+ ];
277
434
  }
278
435
  }
279
436
  // The `comment` mark (w:commentRangeStart/End) is contributed by the comment
@@ -286,7 +443,12 @@ var schema = new Schema({
286
443
  var commentSchema = new Schema({
287
444
  nodes: {
288
445
  doc: { content: "block+" },
289
- paragraph: { group: "block", content: "inline*", parseDOM: [{ tag: "p" }], toDOM: () => ["p", 0] },
446
+ paragraph: {
447
+ group: "block",
448
+ content: "inline*",
449
+ parseDOM: [{ tag: "p" }],
450
+ toDOM: () => ["p", 0]
451
+ },
290
452
  text: { group: "inline" },
291
453
  // @mention: an inline atom carrying the mentioned user's id + display name.
292
454
  // `leafText` lets textContent include "@Name" (search / plain-text preview).
@@ -307,7 +469,10 @@ var commentSchema = new Schema({
307
469
  tag: "span.mention",
308
470
  getAttrs: (el) => {
309
471
  const e = el;
310
- return { id: e.getAttribute("data-id"), label: (e.textContent ?? "").replace(/^@/, "") };
472
+ return {
473
+ id: e.getAttribute("data-id"),
474
+ label: (e.textContent ?? "").replace(/^@/, "")
475
+ };
311
476
  }
312
477
  }
313
478
  ]
@@ -329,7 +494,8 @@ function paragraphStyle(attrs) {
329
494
  if (sp) {
330
495
  if (sp.before) parts.push(`margin-top: ${sp.before}px`);
331
496
  if (sp.after) parts.push(`margin-bottom: ${sp.after}px`);
332
- if (sp.line && sp.lineRule === "auto") parts.push(`line-height: ${sp.line}`);
497
+ if (sp.line && sp.lineRule === "auto")
498
+ parts.push(`line-height: ${sp.line}`);
333
499
  else if (sp.line) parts.push(`line-height: ${sp.line}px`);
334
500
  }
335
501
  return parts.join("; ");
@@ -745,11 +911,16 @@ function propsToMarks(p, ctx) {
745
911
  if (p.italic) marks.push(ctx.schema.marks["em"].create());
746
912
  if (p.underline) marks.push(ctx.schema.marks["underline"].create());
747
913
  if (p.strike) marks.push(ctx.schema.marks["strike"].create());
748
- if (p.color) marks.push(ctx.schema.marks["textColor"].create({ color: p.color }));
749
- if (p.sizePt !== void 0) marks.push(ctx.schema.marks["fontSize"].create({ size: p.sizePt }));
750
- if (p.fontFamily) marks.push(ctx.schema.marks["fontFamily"].create({ family: p.fontFamily }));
751
- if (p.highlight) marks.push(ctx.schema.marks["highlight"].create({ color: p.highlight }));
752
- if (p.vertAlign) marks.push(ctx.schema.marks["vertAlign"].create({ value: p.vertAlign }));
914
+ if (p.color)
915
+ marks.push(ctx.schema.marks["textColor"].create({ color: p.color }));
916
+ if (p.sizePt !== void 0)
917
+ marks.push(ctx.schema.marks["fontSize"].create({ size: p.sizePt }));
918
+ if (p.fontFamily)
919
+ marks.push(ctx.schema.marks["fontFamily"].create({ family: p.fontFamily }));
920
+ if (p.highlight)
921
+ marks.push(ctx.schema.marks["highlight"].create({ color: p.highlight }));
922
+ if (p.vertAlign)
923
+ marks.push(ctx.schema.marks["vertAlign"].create({ value: p.vertAlign }));
753
924
  return marks;
754
925
  }
755
926
  var SYMBOL_MAP = {
@@ -769,7 +940,9 @@ function symbolChar(code) {
769
940
  return Number.isNaN(n) ? "" : String.fromCodePoint(n >= 61440 ? n - 61440 + 32 : n);
770
941
  }
771
942
  function hasPageBreak(run) {
772
- return run.children.some((n) => n.name === "w:br" && attrOf(n, "w:type") === "page");
943
+ return run.children.some(
944
+ (n) => n.name === "w:br" && attrOf(n, "w:type") === "page"
945
+ );
773
946
  }
774
947
  function effectiveChildren(nodes) {
775
948
  const out = [];
@@ -818,8 +991,12 @@ function runInlineNodes(run, marks, ctx) {
818
991
  if (id && ctx.notes.bodies[kind].has(id)) {
819
992
  flush();
820
993
  const num = ctx.notes.ref(kind, id);
821
- const refMarks = [...marks, ctx.schema.marks["vertAlign"].create({ value: "super" })];
822
- if (kind === "footnote") refMarks.push(ctx.schema.marks["footnote"].create({ num }));
994
+ const refMarks = [
995
+ ...marks,
996
+ ctx.schema.marks["vertAlign"].create({ value: "super" })
997
+ ];
998
+ if (kind === "footnote")
999
+ refMarks.push(ctx.schema.marks["footnote"].create({ num }));
823
1000
  out.push(ctx.schema.text(String(num), refMarks));
824
1001
  }
825
1002
  } else if (node.name === "w:br" && attrOf(node, "w:type") !== "page") {
@@ -843,9 +1020,11 @@ function parseAnchorFloat(drawing) {
843
1020
  const posH = child(anchor, "wp:positionH");
844
1021
  if (posH) {
845
1022
  const align = child(posH, "wp:align")?.text.trim();
846
- if (align === "left" || align === "right" || align === "center") float["hAlign"] = align;
1023
+ if (align === "left" || align === "right" || align === "center")
1024
+ float["hAlign"] = align;
847
1025
  const off = emuToPxZero(child(posH, "wp:posOffset")?.text);
848
- if (off !== void 0 && float["hAlign"] === void 0) float["hOffset"] = off;
1026
+ if (off !== void 0 && float["hAlign"] === void 0)
1027
+ float["hOffset"] = off;
849
1028
  const rel = attrOf(posH, "relativeFrom");
850
1029
  float["hRel"] = rel === "page" ? "page" : "margin";
851
1030
  }
@@ -1022,7 +1201,9 @@ function parseShape(run, ctx) {
1022
1201
  function parseTextbox(wsp, ctx) {
1023
1202
  const content = child(child(wsp, "wps:txbx"), "w:txbxContent");
1024
1203
  if (!content) return null;
1025
- const paragraphs = children(content, "w:p").map((p) => parseParagraph(p, ctx).toJSON());
1204
+ const paragraphs = children(content, "w:p").map(
1205
+ (p) => parseParagraph(p, ctx).toJSON()
1206
+ );
1026
1207
  if (paragraphs.length === 0) return null;
1027
1208
  const bodyPr = child(wsp, "wps:bodyPr");
1028
1209
  const ins = (name) => emuToPxZero(attrOf(bodyPr, name));
@@ -1117,7 +1298,10 @@ function flattenOmml(node) {
1117
1298
  function parseParagraph(p, ctx) {
1118
1299
  const pPr = child(p, "w:pPr");
1119
1300
  const pStyleId = attrOf(child(pPr, "w:pStyle"), "w:val");
1120
- const paraBase = mergeRunProps(ctx.styles.docDefaults, ctx.styles.resolveStyle(pStyleId));
1301
+ const paraBase = mergeRunProps(
1302
+ ctx.styles.docDefaults,
1303
+ ctx.styles.resolveStyle(pStyleId)
1304
+ );
1121
1305
  const pPrChain = [
1122
1306
  ctx.styles.docDefaultsPPr,
1123
1307
  ...ctx.styles.resolveStylePPr(pStyleId),
@@ -1150,9 +1334,12 @@ function parseParagraph(p, ctx) {
1150
1334
  } else if (fldType === "end") {
1151
1335
  const kind = fieldKind(field.instr);
1152
1336
  if (kind) {
1153
- inline.push(pageFieldNode(kind, field.resultRuns[0], paraBase, ctx));
1337
+ inline.push(
1338
+ pageFieldNode(kind, field.resultRuns[0], paraBase, ctx)
1339
+ );
1154
1340
  } else {
1155
- for (const r of field.resultRuns) inline.push(...runToInline(r, paraBase, ctx, null));
1341
+ for (const r of field.resultRuns)
1342
+ inline.push(...runToInline(r, paraBase, ctx, null));
1156
1343
  }
1157
1344
  field = null;
1158
1345
  } else if (field.phase === "instr") {
@@ -1169,7 +1356,8 @@ function parseParagraph(p, ctx) {
1169
1356
  if (kind) {
1170
1357
  inline.push(pageFieldNode(kind, resultRuns[0], paraBase, ctx));
1171
1358
  } else {
1172
- for (const r of resultRuns) inline.push(...runToInline(r, paraBase, ctx, null));
1359
+ for (const r of resultRuns)
1360
+ inline.push(...runToInline(r, paraBase, ctx, null));
1173
1361
  }
1174
1362
  } else if (node.name === "w:hyperlink") {
1175
1363
  const rel = attrOf(node, "r:id") ? ctx.rels.get(attrOf(node, "r:id")) : void 0;
@@ -1182,7 +1370,9 @@ function parseParagraph(p, ctx) {
1182
1370
  const text = flattenOmml(node);
1183
1371
  if (text.length > 0) {
1184
1372
  const first = findDescendant(node, "m:r");
1185
- inline.push(ctx.schema.text(text, runMarks(first, paraBase, ctx, null)));
1373
+ inline.push(
1374
+ ctx.schema.text(text, runMarks(first, paraBase, ctx, null))
1375
+ );
1186
1376
  }
1187
1377
  } else if (node.name === "w:commentRangeStart") {
1188
1378
  const id = Number(attrOf(node, "w:id"));
@@ -1199,6 +1389,9 @@ function parseParagraph(p, ctx) {
1199
1389
  if (list) attrs.list = list;
1200
1390
  if (align) attrs.align = align;
1201
1391
  if (heading) attrs.heading = heading;
1392
+ else if (pStyleId && /^(title|subtitle)$/i.test(pStyleId)) {
1393
+ attrs.styleId = pStyleId.toLowerCase() === "title" ? "Title" : "Subtitle";
1394
+ }
1202
1395
  if (indent) attrs.indent = indent;
1203
1396
  if (spacing) attrs.spacing = spacing;
1204
1397
  if (tabs) attrs.tabs = tabs;
@@ -1311,7 +1504,9 @@ function parseList(pPr) {
1311
1504
  return { numId, level: Number.isNaN(ilvl) ? 0 : ilvl };
1312
1505
  }
1313
1506
  function emptyCell(ctx) {
1314
- return ctx.schema.nodes["table_cell"].create(null, [ctx.schema.nodes["paragraph"].create()]);
1507
+ return ctx.schema.nodes["table_cell"].create(null, [
1508
+ ctx.schema.nodes["paragraph"].create()
1509
+ ]);
1315
1510
  }
1316
1511
  function parseCellMargins(tbl) {
1317
1512
  const mar = child(child(tbl, "w:tblPr"), "w:tblCellMar");
@@ -1364,7 +1559,14 @@ function parseBordersEl(bordersEl, sides) {
1364
1559
  }
1365
1560
  return Object.keys(out).length > 0 ? out : null;
1366
1561
  }
1367
- var TABLE_SIDES = ["top", "bottom", "left", "right", "insideH", "insideV"];
1562
+ var TABLE_SIDES = [
1563
+ "top",
1564
+ "bottom",
1565
+ "left",
1566
+ "right",
1567
+ "insideH",
1568
+ "insideV"
1569
+ ];
1368
1570
  var CELL_SIDES = ["top", "bottom", "left", "right"];
1369
1571
  function parseTableBorders(tbl, ctx) {
1370
1572
  const tblPr = child(tbl, "w:tblPr");
@@ -1391,8 +1593,18 @@ function parseTable(tbl, ctx) {
1391
1593
  const vAlign = vAlignVal === "center" || vAlignVal === "bottom" ? vAlignVal : null;
1392
1594
  const borders2 = parseBordersEl(child(tcPr, "w:tcBorders"), CELL_SIDES);
1393
1595
  const content = parseBlocks(tc, ctx);
1394
- if (content.length === 0) content.push(ctx.schema.nodes["paragraph"].create());
1395
- cells.push({ startCol: col, colspan, vMerge, colwidth: widths.length ? widths : null, background, vAlign, borders: borders2, content });
1596
+ if (content.length === 0)
1597
+ content.push(ctx.schema.nodes["paragraph"].create());
1598
+ cells.push({
1599
+ startCol: col,
1600
+ colspan,
1601
+ vMerge,
1602
+ colwidth: widths.length ? widths : null,
1603
+ background,
1604
+ vAlign,
1605
+ borders: borders2,
1606
+ content
1607
+ });
1396
1608
  col += colspan;
1397
1609
  }
1398
1610
  return cells;
@@ -1403,12 +1615,17 @@ function parseTable(tbl, ctx) {
1403
1615
  const header = hdr ? attrOf(hdr, "w:val") !== "false" && attrOf(hdr, "w:val") !== "0" : false;
1404
1616
  const trH = child(trPr, "w:trHeight");
1405
1617
  const hv = attrOf(trH, "w:val");
1406
- const height = hv !== void 0 ? { value: twipsToPx(Number(hv)), exact: attrOf(trH, "w:hRule") === "exact" } : null;
1618
+ const height = hv !== void 0 ? {
1619
+ value: twipsToPx(Number(hv)),
1620
+ exact: attrOf(trH, "w:hRule") === "exact"
1621
+ } : null;
1407
1622
  const cs = child(trPr, "w:cantSplit");
1408
1623
  const cantSplit = cs ? attrOf(cs, "w:val") !== "false" && attrOf(cs, "w:val") !== "0" : false;
1409
1624
  return { header, height, cantSplit };
1410
1625
  });
1411
- const colIndex = logicalRows.map((cells) => new Map(cells.map((c) => [c.startCol, c])));
1626
+ const colIndex = logicalRows.map(
1627
+ (cells) => new Map(cells.map((c) => [c.startCol, c]))
1628
+ );
1412
1629
  const rows = logicalRows.map((cells, r) => {
1413
1630
  const emitted = [];
1414
1631
  for (const cell of cells) {
@@ -1449,7 +1666,8 @@ function parseTable(tbl, ctx) {
1449
1666
  const attrs = {};
1450
1667
  if (cellPadding) attrs["cellPadding"] = cellPadding;
1451
1668
  if (borders) attrs["borders"] = borders;
1452
- if (jc === "center" || jc === "right" || jc === "end") attrs["align"] = jc === "end" ? "right" : jc;
1669
+ if (jc === "center" || jc === "right" || jc === "end")
1670
+ attrs["align"] = jc === "end" ? "right" : jc;
1453
1671
  return ctx.schema.nodes["table"].create(
1454
1672
  Object.keys(attrs).length > 0 ? attrs : null,
1455
1673
  rows.length > 0 ? rows : [ctx.schema.nodes["table_row"].create(null, [emptyCell(ctx)])]
@@ -1515,18 +1733,27 @@ async function readPartRels(zip, partPath) {
1515
1733
  return xml ? parseXml(xml) : void 0;
1516
1734
  }
1517
1735
  async function buildNotesRegistry(zip) {
1518
- const bodies = { footnote: /* @__PURE__ */ new Map(), endnote: /* @__PURE__ */ new Map() };
1736
+ const bodies = {
1737
+ footnote: /* @__PURE__ */ new Map(),
1738
+ endnote: /* @__PURE__ */ new Map()
1739
+ };
1519
1740
  const load = async (path, root, tag, into) => {
1520
1741
  const xml = await readPart(zip, path);
1521
1742
  if (!xml) return;
1522
1743
  for (const note of children(child(parseXml(xml), root), tag)) {
1523
1744
  const id = attrOf(note, "w:id");
1524
1745
  const type = attrOf(note, "w:type");
1525
- if (id === void 0 || Number(id) < 1 || type && type !== "normal") continue;
1746
+ if (id === void 0 || Number(id) < 1 || type && type !== "normal")
1747
+ continue;
1526
1748
  into.set(id, note);
1527
1749
  }
1528
1750
  };
1529
- await load("word/footnotes.xml", "w:footnotes", "w:footnote", bodies.footnote);
1751
+ await load(
1752
+ "word/footnotes.xml",
1753
+ "w:footnotes",
1754
+ "w:footnote",
1755
+ bodies.footnote
1756
+ );
1530
1757
  await load("word/endnotes.xml", "w:endnotes", "w:endnote", bodies.endnote);
1531
1758
  const reg = {
1532
1759
  bodies,
@@ -1561,7 +1788,10 @@ async function buildCommentsRegistry(zip) {
1561
1788
  const ext = /* @__PURE__ */ new Map();
1562
1789
  const extXml = await readPart(zip, "word/commentsExtended.xml");
1563
1790
  if (extXml) {
1564
- for (const ex of children(child(parseXml(extXml), "w15:commentsEx"), "w15:commentEx")) {
1791
+ for (const ex of children(
1792
+ child(parseXml(extXml), "w15:commentsEx"),
1793
+ "w15:commentEx"
1794
+ )) {
1565
1795
  const paraId2 = attrOf(ex, "w15:paraId");
1566
1796
  if (!paraId2) continue;
1567
1797
  const done = attrOf(ex, "w15:done");
@@ -1581,14 +1811,24 @@ function buildCommentsList(ctx) {
1581
1811
  const out = [];
1582
1812
  for (const id of ctx.comments.used) {
1583
1813
  const def = ctx.comments.defs.get(id);
1584
- if (def) out.push({ id, author: def.author, date: def.date, text: collectText(def.body).trim() });
1814
+ if (def)
1815
+ out.push({
1816
+ id,
1817
+ author: def.author,
1818
+ date: def.date,
1819
+ text: collectText(def.body).trim()
1820
+ });
1585
1821
  }
1586
1822
  return out;
1587
1823
  }
1588
1824
  function commentBodyJSON(comment) {
1589
1825
  const paras = children(comment, "w:p").map((p) => {
1590
1826
  const text = collectText(p);
1591
- return commentSchema.node("paragraph", null, text ? [commentSchema.text(text)] : []);
1827
+ return commentSchema.node(
1828
+ "paragraph",
1829
+ null,
1830
+ text ? [commentSchema.text(text)] : []
1831
+ );
1592
1832
  });
1593
1833
  return commentSchema.node("doc", null, paras.length ? paras : [commentSchema.node("paragraph")]).toJSON();
1594
1834
  }
@@ -1608,12 +1848,18 @@ function buildCommentNodes(ctx) {
1608
1848
  if (usedSet.has(cur)) return true;
1609
1849
  return false;
1610
1850
  };
1611
- const ids = [...used.filter((id) => defs.has(id)), ...[...defs.keys()].filter((id) => !usedSet.has(id))];
1851
+ const ids = [
1852
+ ...used.filter((id) => defs.has(id)),
1853
+ ...[...defs.keys()].filter((id) => !usedSet.has(id))
1854
+ ];
1612
1855
  const out = [];
1613
1856
  for (const id of ids) {
1614
1857
  const def = defs.get(id);
1615
1858
  if (!def || !referenced(id)) continue;
1616
- const user = { id: def.author || "unknown", name: def.author || "Unknown" };
1859
+ const user = {
1860
+ id: def.author || "unknown",
1861
+ name: def.author || "Unknown"
1862
+ };
1617
1863
  out.push({
1618
1864
  id,
1619
1865
  parentId: parentOf.get(id) ?? null,
@@ -1627,7 +1873,9 @@ function buildCommentNodes(ctx) {
1627
1873
  }
1628
1874
  function noteBlocks(note, num, ctx) {
1629
1875
  const blocks = parseBlocks(note, ctx);
1630
- const marker = ctx.schema.text(`${num}. `, [ctx.schema.marks["vertAlign"].create({ value: "super" })]);
1876
+ const marker = ctx.schema.text(`${num}. `, [
1877
+ ctx.schema.marks["vertAlign"].create({ value: "super" })
1878
+ ]);
1631
1879
  const first = blocks[0];
1632
1880
  if (first && first.type.name === "paragraph") {
1633
1881
  const kids = [marker];
@@ -1677,7 +1925,10 @@ async function extractMedia(zip) {
1677
1925
  if (!path.startsWith("word/media/")) continue;
1678
1926
  const entry = zip.file(path);
1679
1927
  if (!entry || entry.dir) continue;
1680
- media.set(path, `data:${mimeOf(path)};base64,${await entry.async("base64")}`);
1928
+ media.set(
1929
+ path,
1930
+ `data:${mimeOf(path)};base64,${await entry.async("base64")}`
1931
+ );
1681
1932
  }
1682
1933
  return media;
1683
1934
  }
@@ -1687,11 +1938,16 @@ async function importDocx(input, opts) {
1687
1938
  if (rawDocumentXml === void 0) {
1688
1939
  throw new Error("bapbong-docx: word/document.xml not found in archive");
1689
1940
  }
1690
- const stylesXml = await readPart(zip, "word/styles.xml");
1941
+ const stylesXml2 = await readPart(zip, "word/styles.xml");
1691
1942
  const numberingXml = await readPart(zip, "word/numbering.xml");
1692
1943
  const themeXml = await readPart(zip, "word/theme/theme1.xml");
1693
- const resolveTheme = buildThemeResolver(themeXml ? parseXml(themeXml) : void 0);
1694
- const styles = buildStyleRegistry(stylesXml ? parseXml(stylesXml) : void 0, resolveTheme);
1944
+ const resolveTheme = buildThemeResolver(
1945
+ themeXml ? parseXml(themeXml) : void 0
1946
+ );
1947
+ const styles = buildStyleRegistry(
1948
+ stylesXml2 ? parseXml(stylesXml2) : void 0,
1949
+ resolveTheme
1950
+ );
1695
1951
  const numberingRoot = numberingXml ? parseXml(numberingXml) : void 0;
1696
1952
  const media = await extractMedia(zip);
1697
1953
  const notes = await buildNotesRegistry(zip);
@@ -1740,7 +1996,11 @@ async function importDocx(input, opts) {
1740
1996
  if (!xml) continue;
1741
1997
  const partCtx = makeCtx(buildRels(await readPartRels(zip, partPath)));
1742
1998
  const el = child(parseXml(xml), root);
1743
- store[type] = storyDoc(partCtx, el ? parseBlocks(el, partCtx) : [], ctx.numbering.defs);
1999
+ store[type] = storyDoc(
2000
+ partCtx,
2001
+ el ? parseBlocks(el, partCtx) : [],
2002
+ ctx.numbering.defs
2003
+ );
1744
2004
  }
1745
2005
  };
1746
2006
  await collect("w:headerReference", headers, "w:hdr");
@@ -1813,7 +2073,10 @@ var pxToEmu = (px) => Math.round(px * 9525);
1813
2073
  var commentIdsOf = (node) => node.marks.find((m) => m.type.name === "comment")?.attrs["ids"] ?? [];
1814
2074
  var isInlineLeaf = (node) => node.isText || node.type.name === "image" || node.type.name === "hard_break";
1815
2075
  function esc(s) {
1816
- return s.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
2076
+ return s.replace(
2077
+ /[&<>"]/g,
2078
+ (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]
2079
+ );
1817
2080
  }
1818
2081
  var MIME_EXT = {
1819
2082
  "image/png": "png",
@@ -1836,9 +2099,15 @@ function runProps(marks) {
1836
2099
  const size = byName.get("fontSize")?.attrs["size"];
1837
2100
  if (size != null) out.push(`<w:sz w:val="${Math.round(size * 2)}"/>`);
1838
2101
  const hl = byName.get("highlight")?.attrs["color"];
1839
- if (hl) out.push(`<w:shd w:val="clear" w:color="auto" w:fill="${hl.replace(/^#/, "")}"/>`);
2102
+ if (hl)
2103
+ out.push(
2104
+ `<w:shd w:val="clear" w:color="auto" w:fill="${hl.replace(/^#/, "")}"/>`
2105
+ );
1840
2106
  const va = byName.get("vertAlign")?.attrs["value"];
1841
- if (va) out.push(`<w:vertAlign w:val="${va === "sub" ? "subscript" : "superscript"}"/>`);
2107
+ if (va)
2108
+ out.push(
2109
+ `<w:vertAlign w:val="${va === "sub" ? "subscript" : "superscript"}"/>`
2110
+ );
1842
2111
  return out.length ? `<w:rPr>${out.join("")}</w:rPr>` : "";
1843
2112
  }
1844
2113
  function anchorXml(float, cx, cy, n, graphic) {
@@ -1880,7 +2149,9 @@ function imageXml(node, ctx) {
1880
2149
  const n = ctx.nextId++;
1881
2150
  const rid = `rId${n}`;
1882
2151
  ctx.media.push({ path: `word/media/image${n}.${ext}`, base64: m[2] });
1883
- ctx.rels.push(`<Relationship Id="${rid}" Type="${R_NS}/image" Target="media/image${n}.${ext}"/>`);
2152
+ ctx.rels.push(
2153
+ `<Relationship Id="${rid}" Type="${R_NS}/image" Target="media/image${n}.${ext}"/>`
2154
+ );
1884
2155
  const cx = pxToEmu(node.attrs["width"] ?? 96);
1885
2156
  const cy = pxToEmu(node.attrs["height"] ?? 96);
1886
2157
  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>`;
@@ -1890,7 +2161,8 @@ function inlineXml(node, ctx) {
1890
2161
  if (node.type.name === "image") return imageXml(node, ctx);
1891
2162
  if (node.isText) {
1892
2163
  const fn = node.marks.find((m) => m.type.name === "footnote");
1893
- if (fn) return `<w:r>${runProps(node.marks)}<w:footnoteReference w:id="${fn.attrs["num"]}"/></w:r>`;
2164
+ if (fn)
2165
+ return `<w:r>${runProps(node.marks)}<w:footnoteReference w:id="${fn.attrs["num"]}"/></w:r>`;
1894
2166
  return `<w:r>${runProps(node.marks)}<w:t xml:space="preserve">${esc(node.text ?? "")}</w:t></w:r>`;
1895
2167
  }
1896
2168
  return "";
@@ -1900,9 +2172,12 @@ function inlineUnit(node, ctx) {
1900
2172
  const inner = inlineXml(node, ctx);
1901
2173
  const href = linkHref(node);
1902
2174
  if (!href || !inner) return inner;
1903
- if (href.startsWith("#")) return `<w:hyperlink w:anchor="${esc(href.slice(1))}">${inner}</w:hyperlink>`;
2175
+ if (href.startsWith("#"))
2176
+ return `<w:hyperlink w:anchor="${esc(href.slice(1))}">${inner}</w:hyperlink>`;
1904
2177
  const n = ctx.nextId++;
1905
- ctx.rels.push(`<Relationship Id="rId${n}" Type="${R_NS}/hyperlink" Target="${esc(href)}" TargetMode="External"/>`);
2178
+ ctx.rels.push(
2179
+ `<Relationship Id="rId${n}" Type="${R_NS}/hyperlink" Target="${esc(href)}" TargetMode="External"/>`
2180
+ );
1906
2181
  return `<w:hyperlink r:id="rId${n}">${inner}</w:hyperlink>`;
1907
2182
  }
1908
2183
  function inlineContent(node, ctx) {
@@ -1936,10 +2211,16 @@ function paraProps(node) {
1936
2211
  const a = node.attrs;
1937
2212
  const out = [];
1938
2213
  const heading = a["heading"];
2214
+ const styleId = a["styleId"];
1939
2215
  if (heading) out.push(`<w:pStyle w:val="Heading${heading}"/>`);
2216
+ else if (styleId === "Title" || styleId === "Subtitle")
2217
+ out.push(`<w:pStyle w:val="${styleId}"/>`);
1940
2218
  if (a["pageBreakBefore"]) out.push("<w:pageBreakBefore/>");
1941
2219
  const list = a["list"];
1942
- if (list) out.push(`<w:numPr><w:ilvl w:val="${list.level}"/><w:numId w:val="${esc(list.numId)}"/></w:numPr>`);
2220
+ if (list)
2221
+ out.push(
2222
+ `<w:numPr><w:ilvl w:val="${list.level}"/><w:numId w:val="${esc(list.numId)}"/></w:numPr>`
2223
+ );
1943
2224
  const sp = a["spacing"];
1944
2225
  if (sp) {
1945
2226
  const at = [];
@@ -1947,7 +2228,9 @@ function paraProps(node) {
1947
2228
  if (sp.after != null) at.push(`w:after="${pxToTwips(sp.after)}"`);
1948
2229
  if (sp.line != null) {
1949
2230
  const auto = sp.lineRule === "auto" || sp.lineRule == null;
1950
- at.push(`w:line="${auto ? Math.round(sp.line * 240) : pxToTwips(sp.line)}"`);
2231
+ at.push(
2232
+ `w:line="${auto ? Math.round(sp.line * 240) : pxToTwips(sp.line)}"`
2233
+ );
1951
2234
  at.push(`w:lineRule="${sp.lineRule ?? "auto"}"`);
1952
2235
  }
1953
2236
  if (at.length) out.push(`<w:spacing ${at.join(" ")}/>`);
@@ -1958,11 +2241,13 @@ function paraProps(node) {
1958
2241
  if (ind.left != null) at.push(`w:left="${pxToTwips(ind.left)}"`);
1959
2242
  if (ind.right != null) at.push(`w:right="${pxToTwips(ind.right)}"`);
1960
2243
  if (ind.hanging != null) at.push(`w:hanging="${pxToTwips(ind.hanging)}"`);
1961
- else if (ind.firstLine != null) at.push(`w:firstLine="${pxToTwips(ind.firstLine)}"`);
2244
+ else if (ind.firstLine != null)
2245
+ at.push(`w:firstLine="${pxToTwips(ind.firstLine)}"`);
1962
2246
  if (at.length) out.push(`<w:ind ${at.join(" ")}/>`);
1963
2247
  }
1964
2248
  const align = a["align"];
1965
- if (align) out.push(`<w:jc w:val="${align === "justify" ? "both" : align}"/>`);
2249
+ if (align)
2250
+ out.push(`<w:jc w:val="${align === "justify" ? "both" : align}"/>`);
1966
2251
  return out.join("");
1967
2252
  }
1968
2253
  function paragraphXml(node, ctx, sectPr = "") {
@@ -1970,7 +2255,14 @@ function paragraphXml(node, ctx, sectPr = "") {
1970
2255
  const pPr = props ? `<w:pPr>${props}</w:pPr>` : "";
1971
2256
  return `<w:p>${pPr}${inlineContent(node, ctx)}</w:p>`;
1972
2257
  }
1973
- var TABLE_SIDES2 = ["top", "bottom", "left", "right", "insideH", "insideV"];
2258
+ var TABLE_SIDES2 = [
2259
+ "top",
2260
+ "bottom",
2261
+ "left",
2262
+ "right",
2263
+ "insideH",
2264
+ "insideV"
2265
+ ];
1974
2266
  var CELL_SIDES2 = ["top", "bottom", "left", "right"];
1975
2267
  var BORDER_STYLE_OUT = {
1976
2268
  solid: "single",
@@ -1992,12 +2284,19 @@ function cellXml(cell, ctx) {
1992
2284
  const a = cell.attrs;
1993
2285
  const pr = [];
1994
2286
  const colwidth = a["colwidth"];
1995
- if (colwidth?.length) pr.push(`<w:tcW w:w="${pxToTwips(colwidth.reduce((x, y) => x + y, 0))}" w:type="dxa"/>`);
1996
- if (a["colspan"] > 1) pr.push(`<w:gridSpan w:val="${a["colspan"]}"/>`);
2287
+ if (colwidth?.length)
2288
+ pr.push(
2289
+ `<w:tcW w:w="${pxToTwips(colwidth.reduce((x, y) => x + y, 0))}" w:type="dxa"/>`
2290
+ );
2291
+ if (a["colspan"] > 1)
2292
+ pr.push(`<w:gridSpan w:val="${a["colspan"]}"/>`);
1997
2293
  const borders = a["borders"];
1998
2294
  if (borders) pr.push(bordersXml("w:tcBorders", borders, CELL_SIDES2));
1999
2295
  const bg = a["background"];
2000
- if (bg) pr.push(`<w:shd w:val="clear" w:color="auto" w:fill="${bg.replace(/^#/, "")}"/>`);
2296
+ if (bg)
2297
+ pr.push(
2298
+ `<w:shd w:val="clear" w:color="auto" w:fill="${bg.replace(/^#/, "")}"/>`
2299
+ );
2001
2300
  const vAlign = a["vAlign"];
2002
2301
  if (vAlign) pr.push(`<w:vAlign w:val="${vAlign}"/>`);
2003
2302
  let content = "";
@@ -2010,7 +2309,10 @@ function rowXml(row, ctx) {
2010
2309
  if (row.attrs["header"]) pr.push("<w:tblHeader/>");
2011
2310
  if (row.attrs["cantSplit"]) pr.push("<w:cantSplit/>");
2012
2311
  const h = row.attrs["height"];
2013
- if (h) pr.push(`<w:trHeight w:val="${pxToTwips(h.value)}" w:hRule="${h.exact ? "exact" : "atLeast"}"/>`);
2312
+ if (h)
2313
+ pr.push(
2314
+ `<w:trHeight w:val="${pxToTwips(h.value)}" w:hRule="${h.exact ? "exact" : "atLeast"}"/>`
2315
+ );
2014
2316
  const trPr = pr.length ? `<w:trPr>${pr.join("")}</w:trPr>` : "";
2015
2317
  let cells = "";
2016
2318
  row.forEach((c) => cells += cellXml(c, ctx));
@@ -2029,7 +2331,9 @@ function tableXml(node, ctx) {
2029
2331
  }
2030
2332
  const firstRow = node.firstChild;
2031
2333
  const grid = [];
2032
- firstRow?.forEach((c) => c.attrs["colwidth"]?.forEach((w) => grid.push(w)));
2334
+ firstRow?.forEach(
2335
+ (c) => c.attrs["colwidth"]?.forEach((w) => grid.push(w))
2336
+ );
2033
2337
  const gridXml = grid.length ? `<w:tblGrid>${grid.map((w) => `<w:gridCol w:w="${pxToTwips(w)}"/>`).join("")}</w:tblGrid>` : "";
2034
2338
  let rows = "";
2035
2339
  node.forEach((r) => rows += rowXml(r, ctx));
@@ -2064,10 +2368,14 @@ function commentBodyXml(body, firstParaId) {
2064
2368
  doc.forEach((p, _o, i) => {
2065
2369
  let runs = "";
2066
2370
  p.forEach((inline) => {
2067
- if (inline.isText) runs += `<w:r><w:t xml:space="preserve">${esc(inline.text ?? "")}</w:t></w:r>`;
2068
- else if (inline.type.name === "mention") runs += `<w:r><w:t xml:space="preserve">@${esc(String(inline.attrs["label"] ?? ""))}</w:t></w:r>`;
2371
+ if (inline.isText)
2372
+ runs += `<w:r><w:t xml:space="preserve">${esc(inline.text ?? "")}</w:t></w:r>`;
2373
+ else if (inline.type.name === "mention")
2374
+ runs += `<w:r><w:t xml:space="preserve">@${esc(String(inline.attrs["label"] ?? ""))}</w:t></w:r>`;
2069
2375
  });
2070
- ps.push(`<w:p${i === 0 ? ` w14:paraId="${firstParaId}"` : ""}>${runs}</w:p>`);
2376
+ ps.push(
2377
+ `<w:p${i === 0 ? ` w14:paraId="${firstParaId}"` : ""}>${runs}</w:p>`
2378
+ );
2071
2379
  });
2072
2380
  return ps.join("") || `<w:p w14:paraId="${firstParaId}"/>`;
2073
2381
  }
@@ -2089,16 +2397,65 @@ function commentsExtendedXml(comments) {
2089
2397
  }
2090
2398
  var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2091
2399
  <Relationships xmlns="${PR_NS}"><Relationship Id="rId1" Type="${R_NS}/officeDocument" Target="word/document.xml"/></Relationships>`;
2400
+ var STYLE_DEFS = (() => {
2401
+ 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>`;
2402
+ return {
2403
+ Heading1: heading(1, 48),
2404
+ Heading2: heading(2, 36),
2405
+ Heading3: heading(3, 28),
2406
+ Heading4: heading(4, 24),
2407
+ Heading5: heading(5, 22),
2408
+ Heading6: heading(6, 22),
2409
+ 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>`,
2410
+ 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>`
2411
+ };
2412
+ })();
2413
+ function usedStyleIds(doc) {
2414
+ const used = /* @__PURE__ */ new Set();
2415
+ doc.descendants((n) => {
2416
+ if (n.type.name !== "paragraph") return;
2417
+ const heading = n.attrs["heading"];
2418
+ const styleId = n.attrs["styleId"];
2419
+ if (heading) used.add(`Heading${heading}`);
2420
+ else if (styleId && STYLE_DEFS[styleId]) used.add(styleId);
2421
+ });
2422
+ return used;
2423
+ }
2424
+ function stylesXml(used) {
2425
+ const defs = [...used].map((id) => STYLE_DEFS[id]).join("");
2426
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2427
+ <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>`;
2428
+ }
2429
+ function mergeStyles(xml, used) {
2430
+ const missing = [...used].filter((id) => !xml.includes(`w:styleId="${id}"`));
2431
+ if (!missing.length) return xml;
2432
+ return xml.replace(
2433
+ "</w:styles>",
2434
+ `${missing.map((id) => STYLE_DEFS[id]).join("")}</w:styles>`
2435
+ );
2436
+ }
2092
2437
  function contentTypes(exts, hasComments) {
2093
2438
  const parts = [
2094
2439
  '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>',
2095
2440
  '<Default Extension="xml" ContentType="application/xml"/>'
2096
2441
  ];
2097
- for (const ext of exts) parts.push(`<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`);
2098
- parts.push('<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>');
2442
+ for (const ext of exts)
2443
+ parts.push(
2444
+ `<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`
2445
+ );
2446
+ parts.push(
2447
+ '<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>'
2448
+ );
2449
+ parts.push(
2450
+ '<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>'
2451
+ );
2099
2452
  if (hasComments) {
2100
- parts.push('<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>');
2101
- parts.push('<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>');
2453
+ parts.push(
2454
+ '<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>'
2455
+ );
2456
+ parts.push(
2457
+ '<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>'
2458
+ );
2102
2459
  }
2103
2460
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2104
2461
  <Types xmlns="${CT_NS}">${parts.join("")}</Types>`;
@@ -2106,17 +2463,30 @@ function contentTypes(exts, hasComments) {
2106
2463
  function mergeContentTypes(xml, exts, hasComments) {
2107
2464
  let out = xml;
2108
2465
  const add = (frag, key) => {
2109
- if (!out.includes(`"${key}"`)) out = out.replace("</Types>", `${frag}</Types>`);
2466
+ if (!out.includes(`"${key}"`))
2467
+ out = out.replace("</Types>", `${frag}</Types>`);
2110
2468
  };
2111
- for (const ext of exts) add(`<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`, ext);
2469
+ for (const ext of exts)
2470
+ add(
2471
+ `<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`,
2472
+ ext
2473
+ );
2112
2474
  if (hasComments) {
2113
- add('<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>', "/word/comments.xml");
2114
- add('<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>', "/word/commentsExtended.xml");
2475
+ add(
2476
+ '<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>',
2477
+ "/word/comments.xml"
2478
+ );
2479
+ add(
2480
+ '<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>',
2481
+ "/word/commentsExtended.xml"
2482
+ );
2115
2483
  }
2116
2484
  return out;
2117
2485
  }
2118
2486
  function extractBodySectPr(xml) {
2119
- const all = xml.match(/<w:sectPr\b[^>]*>[\s\S]*?<\/w:sectPr>|<w:sectPr\b[^>]*\/>/g);
2487
+ const all = xml.match(
2488
+ /<w:sectPr\b[^>]*>[\s\S]*?<\/w:sectPr>|<w:sectPr\b[^>]*\/>/g
2489
+ );
2120
2490
  return all ? all[all.length - 1] : "";
2121
2491
  }
2122
2492
  function mergeRels(xml, newRels) {
@@ -2125,7 +2495,10 @@ function mergeRels(xml, newRels) {
2125
2495
  /<Relationship\b[^>]*Target="comments(?:Extended)?\.xml"[^>]*\/>/g,
2126
2496
  ""
2127
2497
  );
2128
- return base.replace("</Relationships>", `${newRels.join("")}</Relationships>`);
2498
+ return base.replace(
2499
+ "</Relationships>",
2500
+ `${newRels.join("")}</Relationships>`
2501
+ );
2129
2502
  }
2130
2503
  async function exportDocx(doc, opts) {
2131
2504
  const comments = doc.attrs["comments"] ?? [];
@@ -2134,7 +2507,8 @@ async function exportDocx(doc, opts) {
2134
2507
  let idx = 0;
2135
2508
  doc.descendants((n) => {
2136
2509
  if (!isInlineLeaf(n)) return;
2137
- for (const id of commentIdsOf(n)) if (knownComments.has(id)) lastRun.set(id, idx);
2510
+ for (const id of commentIdsOf(n))
2511
+ if (knownComments.has(id)) lastRun.set(id, idx);
2138
2512
  idx++;
2139
2513
  });
2140
2514
  const ctx = {
@@ -2149,13 +2523,20 @@ async function exportDocx(doc, opts) {
2149
2523
  };
2150
2524
  const boundaries = sectionBoundaries(doc);
2151
2525
  let body = "";
2152
- doc.forEach((block, _offset, i) => body += blockXml(block, ctx, boundaries.get(i)));
2526
+ doc.forEach(
2527
+ (block, _offset, i) => body += blockXml(block, ctx, boundaries.get(i))
2528
+ );
2153
2529
  const hasComments = comments.length > 0;
2154
2530
  if (hasComments) {
2155
- ctx.rels.push(`<Relationship Id="rIdComments" Type="${R_NS}/comments" Target="comments.xml"/>`);
2156
- ctx.rels.push(`<Relationship Id="rIdCommentsExt" Type="${R_NS}/commentsExtended" Target="commentsExtended.xml"/>`);
2531
+ ctx.rels.push(
2532
+ `<Relationship Id="rIdComments" Type="${R_NS}/comments" Target="comments.xml"/>`
2533
+ );
2534
+ ctx.rels.push(
2535
+ `<Relationship Id="rIdCommentsExt" Type="${R_NS}/commentsExtended" Target="commentsExtended.xml"/>`
2536
+ );
2157
2537
  }
2158
2538
  const zip = new JSZip2();
2539
+ const styleIds = usedStyleIds(doc);
2159
2540
  let sectPr = "";
2160
2541
  if (opts?.carry) {
2161
2542
  const carry = opts.carry;
@@ -2163,18 +2544,29 @@ async function exportDocx(doc, opts) {
2163
2544
  if (!f.dir) zip.file(path, await f.async("uint8array"));
2164
2545
  }
2165
2546
  const ct = await carry.file("[Content_Types].xml")?.async("string");
2166
- zip.file("[Content_Types].xml", ct ? mergeContentTypes(ct, ctx.exts, hasComments) : contentTypes(ctx.exts, hasComments));
2547
+ zip.file(
2548
+ "[Content_Types].xml",
2549
+ ct ? mergeContentTypes(ct, ctx.exts, hasComments) : contentTypes(ctx.exts, hasComments)
2550
+ );
2551
+ const carriedStyles = await carry.file("word/styles.xml")?.async("string");
2552
+ if (carriedStyles)
2553
+ zip.file("word/styles.xml", mergeStyles(carriedStyles, styleIds));
2167
2554
  const rels = await carry.file("word/_rels/document.xml.rels")?.async("string");
2168
2555
  zip.file("word/_rels/document.xml.rels", mergeRels(rels, ctx.rels));
2169
2556
  const origDoc = await carry.file("word/document.xml")?.async("string");
2170
2557
  if (origDoc) sectPr = extractBodySectPr(origDoc);
2171
2558
  } else {
2559
+ ctx.rels.push(
2560
+ `<Relationship Id="rIdStyles" Type="${R_NS}/styles" Target="styles.xml"/>`
2561
+ );
2562
+ zip.file("word/styles.xml", stylesXml(styleIds));
2172
2563
  zip.file("[Content_Types].xml", contentTypes(ctx.exts, hasComments));
2173
2564
  zip.file("_rels/.rels", ROOT_RELS);
2174
- if (ctx.rels.length) {
2175
- zip.file("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2176
- <Relationships xmlns="${PR_NS}">${ctx.rels.join("")}</Relationships>`);
2177
- }
2565
+ zip.file(
2566
+ "word/_rels/document.xml.rels",
2567
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2568
+ <Relationships xmlns="${PR_NS}">${ctx.rels.join("")}</Relationships>`
2569
+ );
2178
2570
  }
2179
2571
  const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2180
2572
  <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>`;
@@ -2183,14 +2575,27 @@ async function exportDocx(doc, opts) {
2183
2575
  zip.file("word/comments.xml", commentsXml(comments));
2184
2576
  zip.file("word/commentsExtended.xml", commentsExtendedXml(comments));
2185
2577
  }
2186
- for (const { path, base64 } of ctx.media) zip.file(path, base64, { base64: true });
2578
+ for (const { path, base64 } of ctx.media)
2579
+ zip.file(path, base64, { base64: true });
2187
2580
  return zip.generateAsync({ type: "uint8array" });
2188
2581
  }
2189
2582
 
2190
2583
  // packages/layout-engine/dist/index.js
2191
- var DEFAULT_FONT = { family: "Arial", sizePt: 11, bold: false, italic: false };
2584
+ var DEFAULT_FONT = {
2585
+ family: "Arial",
2586
+ sizePt: 11,
2587
+ bold: false,
2588
+ italic: false
2589
+ };
2192
2590
  var PT_TO_PX = 96 / 72;
2193
- var HEADING_PT = { 1: 24, 2: 18, 3: 14, 4: 12, 5: 11, 6: 11 };
2591
+ var HEADING_PT = {
2592
+ 1: 24,
2593
+ 2: 18,
2594
+ 3: 14,
2595
+ 4: 12,
2596
+ 5: 11,
2597
+ 6: 11
2598
+ };
2194
2599
  var LINE_HEIGHT_FACTOR = 1.2;
2195
2600
  var BASELINE_FACTOR = 0.8;
2196
2601
  var DEFAULT_TAB_WIDTH = 48;
@@ -2266,11 +2671,13 @@ function resolveImage(node, pos) {
2266
2671
  function paragraphToFlow(node, base, nodePos, allowFloats = false, marker) {
2267
2672
  const contentStart = nodePos + 1;
2268
2673
  const headingLevel2 = node.attrs["heading"];
2269
- const runBase = headingLevel2 ? { ...base, sizePt: HEADING_PT[headingLevel2] ?? base.sizePt, bold: true } : base;
2674
+ const styleId = node.attrs["styleId"];
2675
+ 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;
2270
2676
  const runs = [];
2271
2677
  const floats = [];
2272
2678
  node.forEach((child2, offset) => {
2273
- if (child2.isText) runs.push(resolveRun(child2, runBase, contentStart + offset));
2679
+ if (child2.isText)
2680
+ runs.push(resolveRun(child2, runBase, contentStart + offset));
2274
2681
  else if (child2.type.name === "image") {
2275
2682
  const float = child2.attrs["float"];
2276
2683
  if (float && allowFloats) {
@@ -2297,7 +2704,8 @@ function paragraphToFlow(node, base, nodePos, allowFloats = false, marker) {
2297
2704
  }
2298
2705
  } else if (child2.type.name === "page_field")
2299
2706
  runs.push(resolveField(child2, runBase, contentStart + offset));
2300
- else if (child2.type.name === "hard_break") runs.push({ break: true, pos: contentStart + offset });
2707
+ else if (child2.type.name === "hard_break")
2708
+ runs.push({ break: true, pos: contentStart + offset });
2301
2709
  });
2302
2710
  const list = node.attrs["list"];
2303
2711
  const align = node.attrs["align"];
@@ -2333,8 +2741,15 @@ function markerFor(node, counter) {
2333
2741
  }
2334
2742
  function nodeToBlock(node, base, nodePos, allowFloats = false, counter) {
2335
2743
  if (node.type.name === "paragraph")
2336
- return paragraphToFlow(node, base, nodePos, allowFloats, markerFor(node, counter));
2337
- if (node.type.name === "table") return tableToFlow(node, base, nodePos, counter);
2744
+ return paragraphToFlow(
2745
+ node,
2746
+ base,
2747
+ nodePos,
2748
+ allowFloats,
2749
+ markerFor(node, counter)
2750
+ );
2751
+ if (node.type.name === "table")
2752
+ return tableToFlow(node, base, nodePos, counter);
2338
2753
  return null;
2339
2754
  }
2340
2755
  function tableToFlow(node, base, nodePos, counter) {
@@ -2346,7 +2761,13 @@ function tableToFlow(node, base, nodePos, counter) {
2346
2761
  const cellPos = rowPos + 1 + cellOffset;
2347
2762
  const content = [];
2348
2763
  cellNode.forEach((child2, childOffset) => {
2349
- const block = nodeToBlock(child2, base, cellPos + 1 + childOffset, true, counter);
2764
+ const block = nodeToBlock(
2765
+ child2,
2766
+ base,
2767
+ cellPos + 1 + childOffset,
2768
+ true,
2769
+ counter
2770
+ );
2350
2771
  if (block) content.push(block);
2351
2772
  });
2352
2773
  const a = cellNode.attrs;
@@ -2378,7 +2799,9 @@ function tableToFlow(node, base, nodePos, counter) {
2378
2799
  }
2379
2800
  function toFlowBlocks(doc, defaultFont = {}, allowFloats = true) {
2380
2801
  const base = { ...DEFAULT_FONT, ...defaultFont };
2381
- const counter = createNumberingCounter(doc.attrs["numbering"]);
2802
+ const counter = createNumberingCounter(
2803
+ doc.attrs["numbering"]
2804
+ );
2382
2805
  const blocks = [];
2383
2806
  doc.forEach((node, offset) => {
2384
2807
  const block = nodeToBlock(node, base, offset, allowFloats, counter);
@@ -2388,7 +2811,16 @@ function toFlowBlocks(doc, defaultFont = {}, allowFloats = true) {
2388
2811
  }
2389
2812
  function tokenizeInline(inline, ctx) {
2390
2813
  if ("break" in inline) {
2391
- return [{ isBreak: true, font: ctx.base, width: 0, isSpace: false, pos: inline.pos, size: 1 }];
2814
+ return [
2815
+ {
2816
+ isBreak: true,
2817
+ font: ctx.base,
2818
+ width: 0,
2819
+ isSpace: false,
2820
+ pos: inline.pos,
2821
+ size: 1
2822
+ }
2823
+ ];
2392
2824
  }
2393
2825
  if ("src" in inline) {
2394
2826
  return [
@@ -2499,7 +2931,12 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2499
2931
  return lineLeft + k * tabWidth - x;
2500
2932
  };
2501
2933
  const tabStops = block.tabs ? [...block.tabs].sort((a, b) => a.pos - b.pos) : [];
2502
- const LEADER_CHARS = { dot: ".", hyphen: "-", underscore: "_", middleDot: "\xB7" };
2934
+ const LEADER_CHARS = {
2935
+ dot: ".",
2936
+ hyphen: "-",
2937
+ underscore: "_",
2938
+ middleDot: "\xB7"
2939
+ };
2503
2940
  const MIN_TAB = 2;
2504
2941
  const tabGroup = (ti) => {
2505
2942
  let width = 0;
@@ -2509,7 +2946,8 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2509
2946
  if (t.isTab) break;
2510
2947
  if (decimalPrefix === null && t.text != null && !t.field) {
2511
2948
  const m = /[.,]/.exec(t.text);
2512
- if (m) decimalPrefix = width + measure(t.text.slice(0, m.index), t.font);
2949
+ if (m)
2950
+ decimalPrefix = width + measure(t.text.slice(0, m.index), t.font);
2513
2951
  }
2514
2952
  width += t.width;
2515
2953
  }
@@ -2632,7 +3070,16 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2632
3070
  height = target;
2633
3071
  }
2634
3072
  const painted = firstLine && marker ? [marker, ...segments] : segments;
2635
- emit({ x: startX, width: lineRight - startX, height, baseline, segments: painted, images, from, to });
3073
+ emit({
3074
+ x: startX,
3075
+ width: lineRight - startX,
3076
+ height,
3077
+ baseline,
3078
+ segments: painted,
3079
+ images,
3080
+ from,
3081
+ to
3082
+ });
2636
3083
  prevTo = to;
2637
3084
  lineTokens = [];
2638
3085
  lineWidth = 0;
@@ -2666,10 +3113,14 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2666
3113
  softWrapped = false;
2667
3114
  continue;
2668
3115
  }
2669
- if (token.isSpace && !token.isTab && lineTokens.length === 0 && softWrapped) continue;
3116
+ if (token.isSpace && !token.isTab && lineTokens.length === 0 && softWrapped)
3117
+ continue;
2670
3118
  if (token.isTab) resolveTab(token, lineStart() + lineWidth, ti);
2671
3119
  if (clusterable(token) && clusterFont && sameFont(clusterFont, token.font)) {
2672
- token.width = Math.max(0, measure(clusterText + token.text, token.font) - clusterWidth);
3120
+ token.width = Math.max(
3121
+ 0,
3122
+ measure(clusterText + token.text, token.font) - clusterWidth
3123
+ );
2673
3124
  }
2674
3125
  const cursor = lineStart() + lineWidth;
2675
3126
  if (!token.isSpace && lineTokens.length > 0 && cursor + token.width > lineRight) {
@@ -2677,14 +3128,21 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2677
3128
  resetCluster();
2678
3129
  softWrapped = true;
2679
3130
  if (token.isTab) resolveTab(token, lineStart() + lineWidth, ti);
2680
- else if (clusterable(token)) token.width = measure(token.text, token.font);
3131
+ else if (clusterable(token))
3132
+ token.width = measure(token.text, token.font);
2681
3133
  }
2682
3134
  if (clusterable(token) && lineTokens.length === 0 && token.text.length > 1 && lineStart() + token.width > lineRight) {
2683
3135
  const avail = lineRight - lineStart();
2684
3136
  let n = 1;
2685
- while (n < token.text.length && measure(token.text.slice(0, n + 1), token.font) <= avail) n++;
3137
+ while (n < token.text.length && measure(token.text.slice(0, n + 1), token.font) <= avail)
3138
+ n++;
2686
3139
  const restText = token.text.slice(n);
2687
- const rest = { ...token, text: restText, width: measure(restText, token.font), size: restText.length };
3140
+ const rest = {
3141
+ ...token,
3142
+ text: restText,
3143
+ width: measure(restText, token.font),
3144
+ size: restText.length
3145
+ };
2688
3146
  if (token.pos != null) rest.pos = token.pos + n;
2689
3147
  token.text = token.text.slice(0, n);
2690
3148
  token.size = n;
@@ -2705,7 +3163,10 @@ function wrapParagraph(block, ctx, bandFn, emit) {
2705
3163
  const rotH = rotatedBoxHeight(token.image);
2706
3164
  maxImagePx = Math.max(maxImagePx, rotH);
2707
3165
  maxAscent = Math.max(maxAscent, token.image.height / 2 + rotH / 2);
2708
- maxDescent = Math.max(maxDescent, Math.max(0, (rotH - token.image.height) / 2));
3166
+ maxDescent = Math.max(
3167
+ maxDescent,
3168
+ Math.max(0, (rotH - token.image.height) / 2)
3169
+ );
2709
3170
  } else {
2710
3171
  maxFontPx = Math.max(maxFontPx, sizePx(token.font));
2711
3172
  if (metrics) {
@@ -2758,7 +3219,13 @@ function shiftTableX(table, dx) {
2758
3219
  }
2759
3220
  var TEXTBOX_INSET = { l: 10, t: 5, r: 10, b: 5 };
2760
3221
  function resolveFloat(f, x, y, ctx) {
2761
- const rf = { x, y, width: f.width, height: f.height, src: f.src };
3222
+ const rf = {
3223
+ x,
3224
+ y,
3225
+ width: f.width,
3226
+ height: f.height,
3227
+ src: f.src
3228
+ };
2762
3229
  if (f.pos != null) rf.pos = f.pos;
2763
3230
  if (f.rotation) rf.rotation = f.rotation;
2764
3231
  if (f.shape) rf.shape = f.shape;
@@ -2804,7 +3271,8 @@ function eachCell(rows, ncols, visit) {
2804
3271
  for (const cell of rows[r].cells) {
2805
3272
  while (col < ncols && spanned[col] > 0) col++;
2806
3273
  visit(r, cell, col);
2807
- for (let k = 0; k < cell.colspan && col + k < ncols; k++) spanned[col + k] = cell.rowspan;
3274
+ for (let k = 0; k < cell.colspan && col + k < ncols; k++)
3275
+ spanned[col + k] = cell.rowspan;
2808
3276
  col += cell.colspan;
2809
3277
  }
2810
3278
  for (let i = 0; i < ncols; i++) if (spanned[i] > 0) spanned[i]--;
@@ -2814,14 +3282,18 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2814
3282
  const avail = contentRight - contentLeft;
2815
3283
  const nrows = table.rows.length;
2816
3284
  const ncols = table.rows.reduce(
2817
- (m, r) => Math.max(m, r.cells.reduce((s, c) => s + c.colspan, 0)),
3285
+ (m, r) => Math.max(
3286
+ m,
3287
+ r.cells.reduce((s, c) => s + c.colspan, 0)
3288
+ ),
2818
3289
  0
2819
3290
  );
2820
3291
  const colWidths = new Array(ncols).fill(0);
2821
3292
  eachCell(table.rows, ncols, (_r, cell, startCol) => {
2822
3293
  if (cell.colwidth && cell.colwidth.length === cell.colspan) {
2823
3294
  for (let k = 0; k < cell.colspan && startCol + k < ncols; k++) {
2824
- if (colWidths[startCol + k] === 0) colWidths[startCol + k] = cell.colwidth[k];
3295
+ if (colWidths[startCol + k] === 0)
3296
+ colWidths[startCol + k] = cell.colwidth[k];
2825
3297
  }
2826
3298
  }
2827
3299
  });
@@ -2829,7 +3301,8 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2829
3301
  const unknown = colWidths.filter((w) => w === 0).length;
2830
3302
  if (unknown > 0) {
2831
3303
  const share = Math.max(0, (avail - known) / unknown);
2832
- for (let i = 0; i < ncols; i++) if (colWidths[i] === 0) colWidths[i] = share;
3304
+ for (let i = 0; i < ncols; i++)
3305
+ if (colWidths[i] === 0) colWidths[i] = share;
2833
3306
  }
2834
3307
  const natural = colWidths.reduce((s, w) => s + w, 0);
2835
3308
  if (natural > avail && natural > 0) {
@@ -2849,9 +3322,15 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2849
3322
  const cellDrafts = [];
2850
3323
  eachCell(table.rows, ncols, (r, cell, col) => {
2851
3324
  let cellWidth = 0;
2852
- for (let k = 0; k < cell.colspan && col + k < ncols; k++) cellWidth += colWidths[col + k];
3325
+ for (let k = 0; k < cell.colspan && col + k < ncols; k++)
3326
+ cellWidth += colWidths[col + k];
2853
3327
  const cellLeft = colX[col];
2854
- const flow = layoutFlow(cell.content, cellLeft + pad.left, cellLeft + cellWidth - pad.right, ctx);
3328
+ const flow = layoutFlow(
3329
+ cell.content,
3330
+ cellLeft + pad.left,
3331
+ cellLeft + cellWidth - pad.right,
3332
+ ctx
3333
+ );
2855
3334
  cellDrafts.push({
2856
3335
  startRow: r,
2857
3336
  startCol: col,
@@ -2871,14 +3350,18 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2871
3350
  const rowHeight = new Array(nrows).fill(0);
2872
3351
  for (const c of cellDrafts) {
2873
3352
  if (c.rowspan === 1) {
2874
- rowHeight[c.startRow] = Math.max(rowHeight[c.startRow], c.contentHeight + pad.top + pad.bottom);
3353
+ rowHeight[c.startRow] = Math.max(
3354
+ rowHeight[c.startRow],
3355
+ c.contentHeight + pad.top + pad.bottom
3356
+ );
2875
3357
  }
2876
3358
  }
2877
3359
  for (const c of cellDrafts) {
2878
3360
  if (c.rowspan > 1) {
2879
3361
  const need = c.contentHeight + pad.top + pad.bottom;
2880
3362
  let span = 0;
2881
- for (let r = c.startRow; r < c.startRow + c.rowspan && r < nrows; r++) span += rowHeight[r];
3363
+ for (let r = c.startRow; r < c.startRow + c.rowspan && r < nrows; r++)
3364
+ span += rowHeight[r];
2882
3365
  if (need > span) {
2883
3366
  const last = Math.min(c.startRow + c.rowspan - 1, nrows - 1);
2884
3367
  rowHeight[last] += need - span;
@@ -2893,7 +3376,8 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2893
3376
  for (let r = 0; r < nrows; r++) rowY[r + 1] = rowY[r] + rowHeight[r];
2894
3377
  const cells = cellDrafts.map((c) => {
2895
3378
  let height = 0;
2896
- for (let r = c.startRow; r < c.startRow + c.rowspan && r < nrows; r++) height += rowHeight[r];
3379
+ for (let r = c.startRow; r < c.startRow + c.rowspan && r < nrows; r++)
3380
+ height += rowHeight[r];
2897
3381
  const slack = Math.max(0, height - pad.top - pad.bottom - c.contentHeight);
2898
3382
  const vOffset = c.vAlign === "bottom" ? slack : c.vAlign === "center" ? slack / 2 : 0;
2899
3383
  const dy = rowY[c.startRow] + pad.top + vOffset;
@@ -2911,19 +3395,30 @@ function layoutTable(table, contentLeft, contentRight, ctx) {
2911
3395
  if (c.background) cell.background = c.background;
2912
3396
  if (c.borders) cell.borders = c.borders;
2913
3397
  if (c.tables.length > 0) cell.tables = c.tables;
2914
- if (c.floats.length > 0) cell.floats = c.floats.map((f) => ({ ...f, y: f.y + dy }));
3398
+ if (c.floats.length > 0)
3399
+ cell.floats = c.floats.map((f) => ({ ...f, y: f.y + dy }));
2915
3400
  return cell;
2916
3401
  });
2917
- const resolved = { x: contentLeft + xShift, y: 0, width: tableWidth, height: rowY[nrows], cells };
3402
+ const resolved = {
3403
+ x: contentLeft + xShift,
3404
+ y: 0,
3405
+ width: tableWidth,
3406
+ height: rowY[nrows],
3407
+ cells
3408
+ };
2918
3409
  if (table.borders) resolved.borders = table.borders;
2919
3410
  let headerRows = 0;
2920
3411
  while (headerRows < nrows && table.rows[headerRows].header) headerRows++;
2921
3412
  if (headerRows > 0 && headerRows < nrows) {
2922
3413
  const headerBottom = rowY[headerRows];
2923
- const spansOut = cells.some((c) => c.y < headerBottom && c.y + c.height > headerBottom);
3414
+ const spansOut = cells.some(
3415
+ (c) => c.y < headerBottom && c.y + c.height > headerBottom
3416
+ );
2924
3417
  if (!spansOut) resolved.headerBottom = headerBottom;
2925
3418
  }
2926
- const cantSplitBands = table.rows.map((row, r) => row.cantSplit ? { top: rowY[r], bottom: rowY[r + 1] } : null).filter((b) => b !== null);
3419
+ const cantSplitBands = table.rows.map(
3420
+ (row, r) => row.cantSplit ? { top: rowY[r], bottom: rowY[r + 1] } : null
3421
+ ).filter((b) => b !== null);
2927
3422
  if (cantSplitBands.length > 0) resolved.cantSplitBands = cantSplitBands;
2928
3423
  return resolved;
2929
3424
  }
@@ -2956,7 +3451,10 @@ function cloneTable(t) {
2956
3451
  return { ...t, cells: t.cells.map(cloneCell) };
2957
3452
  }
2958
3453
  function cloneCell(cell) {
2959
- const copy = { ...cell, lines: cell.lines.map((l) => ({ ...l })) };
3454
+ const copy = {
3455
+ ...cell,
3456
+ lines: cell.lines.map((l) => ({ ...l }))
3457
+ };
2960
3458
  if (cell.tables) copy.tables = cell.tables.map(cloneTable);
2961
3459
  if (cell.floats) copy.floats = cell.floats.map((f) => ({ ...f }));
2962
3460
  return copy;
@@ -2992,11 +3490,19 @@ function splitTableAt(table, cut) {
2992
3490
  } else {
2993
3491
  const c = straddlers.get(cell);
2994
3492
  const topLines = cell.lines.filter((l) => l.y + l.height <= cut);
2995
- const topTables = (cell.tables ?? []).filter((t) => t.y + t.height <= cut);
2996
- const topCell = { ...cell, height: cut - cell.y, lines: topLines };
3493
+ const topTables = (cell.tables ?? []).filter(
3494
+ (t) => t.y + t.height <= cut
3495
+ );
3496
+ const topCell = {
3497
+ ...cell,
3498
+ height: cut - cell.y,
3499
+ lines: topLines
3500
+ };
2997
3501
  if (topTables.length > 0) topCell.tables = topTables;
2998
3502
  else delete topCell.tables;
2999
- const topFloats = (cell.floats ?? []).filter((f) => f.y + f.height <= cut);
3503
+ const topFloats = (cell.floats ?? []).filter(
3504
+ (f) => f.y + f.height <= cut
3505
+ );
3000
3506
  if (topFloats.length > 0) topCell.floats = topFloats;
3001
3507
  else delete topCell.floats;
3002
3508
  topCells.push(topCell);
@@ -3019,7 +3525,13 @@ function splitTableAt(table, cut) {
3019
3525
  restCells.push(contCell);
3020
3526
  }
3021
3527
  }
3022
- const top = { x: table.x, y: 0, width: table.width, height: cut, cells: topCells };
3528
+ const top = {
3529
+ x: table.x,
3530
+ y: 0,
3531
+ width: table.width,
3532
+ height: cut,
3533
+ cells: topCells
3534
+ };
3023
3535
  const rest = {
3024
3536
  x: table.x,
3025
3537
  y: 0,
@@ -3041,10 +3553,14 @@ function cloneHeaderCells(table, headerBottom) {
3041
3553
  tables: void 0,
3042
3554
  floats: cell.floats?.map((f) => ({ ...f })),
3043
3555
  lines: cell.lines.map((l) => {
3044
- const line = { ...l, segments: l.segments.map((s) => ({ ...s, pos: void 0 })) };
3556
+ const line = {
3557
+ ...l,
3558
+ segments: l.segments.map((s) => ({ ...s, pos: void 0 }))
3559
+ };
3045
3560
  delete line.from;
3046
3561
  delete line.to;
3047
- if (line.images) line.images = line.images.map((im) => ({ ...im, pos: void 0 }));
3562
+ if (line.images)
3563
+ line.images = line.images.map((im) => ({ ...im, pos: void 0 }));
3048
3564
  return line;
3049
3565
  })
3050
3566
  }));
@@ -3121,13 +3637,15 @@ function placeBlocks(items, config, ctx, band, footnotes) {
3121
3637
  for (const l of ls)
3122
3638
  for (const s of l.segments) {
3123
3639
  const n = s.footnoteRef;
3124
- if (n != null && footnotes?.has(n) && !pageFnSet.has(n) && !out.includes(n)) out.push(n);
3640
+ if (n != null && footnotes?.has(n) && !pageFnSet.has(n) && !out.includes(n))
3641
+ out.push(n);
3125
3642
  }
3126
3643
  };
3127
3644
  for (const c of t.cells) {
3128
3645
  if (c.y >= maxY) continue;
3129
3646
  scan(c.lines);
3130
- if (c.tables) for (const nt of c.tables) for (const nc of nt.cells) scan(nc.lines);
3647
+ if (c.tables)
3648
+ for (const nt of c.tables) for (const nc of nt.cells) scan(nc.lines);
3131
3649
  }
3132
3650
  return out;
3133
3651
  };
@@ -3156,10 +3674,16 @@ function placeBlocks(items, config, ctx, band, footnotes) {
3156
3674
  return { separatorY: areaTop + FOOTNOTE_AREA_GAP / 2, lines: out };
3157
3675
  };
3158
3676
  const finalizePage = () => {
3159
- const resolved = { index: pages.length, width: page.width, height: page.height, lines };
3677
+ const resolved = {
3678
+ index: pages.length,
3679
+ width: page.width,
3680
+ height: page.height,
3681
+ lines
3682
+ };
3160
3683
  if (tables.length > 0) resolved.tables = tables;
3161
3684
  if (pageFloats.length > 0) resolved.floats = pageFloats;
3162
- if (pageFnNums.length > 0) resolved.footnotes = buildFootnoteArea(pageFnNums);
3685
+ if (pageFnNums.length > 0)
3686
+ resolved.footnotes = buildFootnoteArea(pageFnNums);
3163
3687
  pages.push(resolved);
3164
3688
  lines = [];
3165
3689
  tables = [];
@@ -3249,7 +3773,9 @@ function placeBlocks(items, config, ctx, band, footnotes) {
3249
3773
  }
3250
3774
  const b = bandAt(y, estH);
3251
3775
  if (b) return b;
3252
- const blockers = exclusions.filter((ex) => ex.top < y + estH && ex.bottom > y);
3776
+ const blockers = exclusions.filter(
3777
+ (ex) => ex.top < y + estH && ex.bottom > y
3778
+ );
3253
3779
  if (blockers.length === 0) return { left: colX0(), right: colX1() };
3254
3780
  y = Math.min(...blockers.map((ex) => ex.bottom));
3255
3781
  }
@@ -3293,7 +3819,9 @@ function placeBlocks(items, config, ctx, band, footnotes) {
3293
3819
  }
3294
3820
  const drafts = item.para.drafts;
3295
3821
  const draftsHeight = drafts?.reduce((s, d) => s + d.height, 0) ?? 0;
3296
- const floatsAhead = exclusions.some((ex) => ex.bottom > y && ex.top < y + draftsHeight);
3822
+ const floatsAhead = exclusions.some(
3823
+ (ex) => ex.bottom > y && ex.top < y + draftsHeight
3824
+ );
3297
3825
  if (drafts && !floatsAhead) {
3298
3826
  for (const d of drafts) emitLine(d);
3299
3827
  } else {
@@ -3389,16 +3917,25 @@ function shiftDrafts(drafts, delta) {
3389
3917
  ...d,
3390
3918
  from: d.from != null ? d.from + delta : d.from,
3391
3919
  to: d.to != null ? d.to + delta : d.to,
3392
- segments: d.segments.map((s) => s.pos != null ? { ...s, pos: s.pos + delta } : s),
3393
- images: d.images.map((im) => im.pos != null ? { ...im, pos: im.pos + delta } : im)
3920
+ segments: d.segments.map(
3921
+ (s) => s.pos != null ? { ...s, pos: s.pos + delta } : s
3922
+ ),
3923
+ images: d.images.map(
3924
+ (im) => im.pos != null ? { ...im, pos: im.pos + delta } : im
3925
+ )
3394
3926
  }));
3395
3927
  }
3396
3928
  function cloneLineShifted(l, delta) {
3397
3929
  const out = {
3398
3930
  ...l,
3399
- segments: l.segments.map((s) => s.pos != null ? { ...s, pos: s.pos + delta } : { ...s })
3931
+ segments: l.segments.map(
3932
+ (s) => s.pos != null ? { ...s, pos: s.pos + delta } : { ...s }
3933
+ )
3400
3934
  };
3401
- if (l.images) out.images = l.images.map((im) => im.pos != null ? { ...im, pos: im.pos + delta } : { ...im });
3935
+ if (l.images)
3936
+ out.images = l.images.map(
3937
+ (im) => im.pos != null ? { ...im, pos: im.pos + delta } : { ...im }
3938
+ );
3402
3939
  if (l.from != null) out.from = l.from + delta;
3403
3940
  if (l.to != null) out.to = l.to + delta;
3404
3941
  return out;
@@ -3441,7 +3978,10 @@ function layVariants(docs, fn) {
3441
3978
  return out;
3442
3979
  }
3443
3980
  function maxBandHeight(bands) {
3444
- return Math.max(0, ...[bands.default, bands.first, bands.even].filter(Boolean).map((b) => b.height));
3981
+ return Math.max(
3982
+ 0,
3983
+ ...[bands.default, bands.first, bands.even].filter(Boolean).map((b) => b.height)
3984
+ );
3445
3985
  }
3446
3986
  function anyBandHasFields(bands) {
3447
3987
  return [bands.default, bands.first, bands.even].some(
@@ -3467,9 +4007,17 @@ function layoutChrome(doc, topY, left, right, ctx) {
3467
4007
  const lines = flow.lines.map((l) => ({ ...l, y: l.y + topY }));
3468
4008
  flow.tables.forEach((t) => offsetTable(t, topY));
3469
4009
  stripPositions(lines, flow.tables);
3470
- const band = { lines, tables: flow.tables, height: flow.height };
4010
+ const band = {
4011
+ lines,
4012
+ tables: flow.tables,
4013
+ height: flow.height
4014
+ };
3471
4015
  if (flow.floats.length > 0)
3472
- band.floats = flow.floats.map((f) => ({ ...f, y: f.y + topY, pos: void 0 }));
4016
+ band.floats = flow.floats.map((f) => ({
4017
+ ...f,
4018
+ y: f.y + topY,
4019
+ pos: void 0
4020
+ }));
3473
4021
  return band;
3474
4022
  }
3475
4023
  function layoutFooterChrome(doc, pageHeight, left, right, ctx) {
@@ -3481,12 +4029,21 @@ function layoutFooterChrome(doc, pageHeight, left, right, ctx) {
3481
4029
  height: flow.height
3482
4030
  };
3483
4031
  band.tables.forEach((t) => offsetTable(t, topY));
3484
- if (flow.floats) band.floats = flow.floats.map((f) => ({ ...f, y: f.y + topY }));
4032
+ if (flow.floats)
4033
+ band.floats = flow.floats.map((f) => ({ ...f, y: f.y + topY }));
3485
4034
  return band;
3486
4035
  }
3487
4036
  function layoutFootnoteBody(doc, left, right, ctx) {
3488
- const fnCtx = { ...ctx, base: { ...ctx.base, sizePt: ctx.base.sizePt * FOOTNOTE_FONT_SCALE } };
3489
- const flow = layoutFlow(toFlowBlocks(doc, fnCtx.base, false), left, right, fnCtx);
4037
+ const fnCtx = {
4038
+ ...ctx,
4039
+ base: { ...ctx.base, sizePt: ctx.base.sizePt * FOOTNOTE_FONT_SCALE }
4040
+ };
4041
+ const flow = layoutFlow(
4042
+ toFlowBlocks(doc, fnCtx.base, false),
4043
+ left,
4044
+ right,
4045
+ fnCtx
4046
+ );
3490
4047
  stripPositions(flow.lines, flow.tables);
3491
4048
  return { lines: flow.lines, height: flow.height };
3492
4049
  }
@@ -3495,10 +4052,24 @@ function layout(doc, config, cache, chrome, footnotes) {
3495
4052
  const { page } = config;
3496
4053
  const left = page.margin.left;
3497
4054
  const right = page.width - page.margin.right;
3498
- const headerDocs = { default: chrome?.header, first: chrome?.headerFirst, even: chrome?.headerEven };
3499
- const footerDocs = { default: chrome?.footer, first: chrome?.footerFirst, even: chrome?.footerEven };
3500
- const layHeaders = (c) => layVariants(headerDocs, (d) => layoutChrome(d, CHROME_DISTANCE, left, right, c));
3501
- const layFooters = (c) => layVariants(footerDocs, (d) => layoutFooterChrome(d, page.height, left, right, c));
4055
+ const headerDocs = {
4056
+ default: chrome?.header,
4057
+ first: chrome?.headerFirst,
4058
+ even: chrome?.headerEven
4059
+ };
4060
+ const footerDocs = {
4061
+ default: chrome?.footer,
4062
+ first: chrome?.footerFirst,
4063
+ even: chrome?.footerEven
4064
+ };
4065
+ const layHeaders = (c) => layVariants(
4066
+ headerDocs,
4067
+ (d) => layoutChrome(d, CHROME_DISTANCE, left, right, c)
4068
+ );
4069
+ const layFooters = (c) => layVariants(
4070
+ footerDocs,
4071
+ (d) => layoutFooterChrome(d, page.height, left, right, c)
4072
+ );
3502
4073
  let headers = layHeaders(ctx);
3503
4074
  let footers = layFooters(ctx);
3504
4075
  let top = page.margin.top;
@@ -3507,25 +4078,46 @@ function layout(doc, config, cache, chrome, footnotes) {
3507
4078
  top = Math.max(top, CHROME_DISTANCE + maxBandHeight(headers));
3508
4079
  }
3509
4080
  if (footers.default || footers.first || footers.even) {
3510
- bottom = Math.min(bottom, page.height - CHROME_DISTANCE - maxBandHeight(footers));
4081
+ bottom = Math.min(
4082
+ bottom,
4083
+ page.height - CHROME_DISTANCE - maxBandHeight(footers)
4084
+ );
3511
4085
  }
3512
- const counter = createNumberingCounter(doc.attrs["numbering"]);
4086
+ const counter = createNumberingCounter(
4087
+ doc.attrs["numbering"]
4088
+ );
3513
4089
  const sections = doc.attrs["sections"] ?? [
3514
- { blockCount: doc.childCount, columns: { count: 1, gap: 0 }, newPage: true }
4090
+ {
4091
+ blockCount: doc.childCount,
4092
+ columns: { count: 1, gap: 0 },
4093
+ newPage: true
4094
+ }
3515
4095
  ];
3516
4096
  const blockSection = [];
3517
4097
  for (const sec of sections) {
3518
4098
  for (let k = 0; k < sec.blockCount; k++) {
3519
- blockSection.push({ columns: sec.columns, start: k === 0, newPage: sec.newPage });
4099
+ blockSection.push({
4100
+ columns: sec.columns,
4101
+ start: k === 0,
4102
+ newPage: sec.newPage
4103
+ });
3520
4104
  }
3521
4105
  }
3522
4106
  const lastSec = sections[sections.length - 1];
3523
4107
  while (blockSection.length < doc.childCount) {
3524
- blockSection.push({ columns: lastSec.columns, start: false, newPage: lastSec.newPage });
4108
+ blockSection.push({
4109
+ columns: lastSec.columns,
4110
+ start: false,
4111
+ newPage: lastSec.newPage
4112
+ });
3525
4113
  }
3526
4114
  const items = [];
3527
4115
  doc.forEach((node, offset, index) => {
3528
- const bs = blockSection[index] ?? { columns: { count: 1, gap: 0 }, start: false, newPage: true };
4116
+ const bs = blockSection[index] ?? {
4117
+ columns: { count: 1, gap: 0 },
4118
+ start: false,
4119
+ newPage: true
4120
+ };
3529
4121
  const colRight = left + columnWidth(right - left, bs.columns);
3530
4122
  const tag = (item) => {
3531
4123
  if (bs.start) item.section = { ...bs.columns, newPage: bs.newPage };
@@ -3558,17 +4150,35 @@ function layout(doc, config, cache, chrome, footnotes) {
3558
4150
  }
3559
4151
  const flow = paragraphToFlow(node, ctx.base, offset, true, marker);
3560
4152
  const drafts = layoutParagraph(flow, left, colRight, ctx);
3561
- cache?.paragraphs.set(node, { left, right: colRight, basePos: contentStart, marker, drafts });
4153
+ cache?.paragraphs.set(node, {
4154
+ left,
4155
+ right: colRight,
4156
+ basePos: contentStart,
4157
+ marker,
4158
+ drafts
4159
+ });
3562
4160
  items.push(tag({ para: { ...para(drafts), getFlow: () => flow } }));
3563
4161
  } else if (node.type.name === "table") {
3564
4162
  const hit = cache?.tables.get(node);
3565
4163
  if (hit && hit.left === left && hit.right === colRight) {
3566
- items.push(tag({ table: cloneTableShifted(hit.table, offset - hit.basePos) }));
4164
+ items.push(
4165
+ tag({ table: cloneTableShifted(hit.table, offset - hit.basePos) })
4166
+ );
3567
4167
  return;
3568
4168
  }
3569
- const table = layoutTable(tableToFlow(node, ctx.base, offset, counter), left, colRight, ctx);
4169
+ const table = layoutTable(
4170
+ tableToFlow(node, ctx.base, offset, counter),
4171
+ left,
4172
+ colRight,
4173
+ ctx
4174
+ );
3570
4175
  if (cache && !tableHasList(node)) {
3571
- cache.tables.set(node, { left, right: colRight, basePos: offset, table });
4176
+ cache.tables.set(node, {
4177
+ left,
4178
+ right: colRight,
4179
+ basePos: offset,
4180
+ table
4181
+ });
3572
4182
  items.push(tag({ table: cloneTableShifted(table, 0) }));
3573
4183
  } else {
3574
4184
  items.push(tag({ table }));
@@ -3586,7 +4196,10 @@ function layout(doc, config, cache, chrome, footnotes) {
3586
4196
  assignSectionHeights(items);
3587
4197
  const resolved = placeBlocks(items, config, ctx, { top, bottom }, fnMap);
3588
4198
  if (anyBandHasFields(headers) || anyBandHasFields(footers)) {
3589
- const fieldCtx = { ...ctx, fieldPlaceholder: String(resolved.pages.length) };
4199
+ const fieldCtx = {
4200
+ ...ctx,
4201
+ fieldPlaceholder: String(resolved.pages.length)
4202
+ };
3590
4203
  headers = layHeaders(fieldCtx);
3591
4204
  footers = layFooters(fieldCtx);
3592
4205
  }
@@ -3597,7 +4210,10 @@ function layout(doc, config, cache, chrome, footnotes) {
3597
4210
  if (footers.first) resolved.pageFooterFirst = footers.first;
3598
4211
  if (footers.even) resolved.pageFooterEven = footers.even;
3599
4212
  if (chrome?.titlePg || chrome?.evenAndOdd) {
3600
- resolved.chromeSelect = { titlePg: !!chrome.titlePg, evenAndOdd: !!chrome.evenAndOdd };
4213
+ resolved.chromeSelect = {
4214
+ titlePg: !!chrome.titlePg,
4215
+ evenAndOdd: !!chrome.evenAndOdd
4216
+ };
3601
4217
  }
3602
4218
  return resolved;
3603
4219
  }
@@ -4816,7 +5432,12 @@ function collectFontFamilies(...docs) {
4816
5432
  import { baseKeymap } from "prosemirror-commands";
4817
5433
  import { history, redo, undo } from "prosemirror-history";
4818
5434
  import { keymap } from "prosemirror-keymap";
4819
- import { EditorState, Plugin, PluginKey, TextSelection } from "prosemirror-state";
5435
+ import {
5436
+ EditorState,
5437
+ Plugin,
5438
+ PluginKey,
5439
+ TextSelection
5440
+ } from "prosemirror-state";
4820
5441
  import { canSplit } from "prosemirror-transform";
4821
5442
  import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
4822
5443
  function moveCaretCommand(compute, extend = false) {
@@ -4835,7 +5456,10 @@ var splitListItem = (state, dispatch) => {
4835
5456
  if ($from.parent !== $to.parent) return false;
4836
5457
  if (parent.content.size === 0) {
4837
5458
  dispatch?.(
4838
- state.tr.setNodeMarkup($from.before(), null, { ...parent.attrs, list: null })
5459
+ state.tr.setNodeMarkup($from.before(), null, {
5460
+ ...parent.attrs,
5461
+ list: null
5462
+ })
4839
5463
  );
4840
5464
  return true;
4841
5465
  }
@@ -4911,6 +5535,7 @@ var InputBridge = class {
4911
5535
  this.dom.appendChild(this.host);
4912
5536
  this.view = new EditorView(this.host, {
4913
5537
  state: createEditingState(options.doc, options.keys),
5538
+ handlePaste: options.handlePaste,
4914
5539
  dispatchTransaction: (tr) => {
4915
5540
  const state = this.view.state.apply(tr);
4916
5541
  this.view.updateState(state);
@@ -4934,7 +5559,10 @@ var InputBridge = class {
4934
5559
  setSelection(anchor, head = anchor) {
4935
5560
  const { doc } = this.view.state;
4936
5561
  const clamp2 = (p) => Math.max(0, Math.min(p, doc.content.size));
4937
- const sel = TextSelection.between(doc.resolve(clamp2(anchor)), doc.resolve(clamp2(head)));
5562
+ const sel = TextSelection.between(
5563
+ doc.resolve(clamp2(anchor)),
5564
+ doc.resolve(clamp2(head))
5565
+ );
4938
5566
  this.view.dispatch(this.view.state.tr.setSelection(sel));
4939
5567
  }
4940
5568
  /** Select the word around `pos` (double-click); falls back to a collapsed
@@ -5749,7 +6377,8 @@ function setAlign(align) {
5749
6377
  const tr = state.tr;
5750
6378
  let changed = false;
5751
6379
  state.doc.nodesBetween(from, to, (node, pos) => {
5752
- if (node.type.name !== "paragraph" || node.attrs["align"] === align) return;
6380
+ if (node.type.name !== "paragraph" || node.attrs["align"] === align)
6381
+ return;
5753
6382
  tr.setNodeAttribute(pos, "align", align);
5754
6383
  changed = true;
5755
6384
  });
@@ -5774,7 +6403,9 @@ function toggleHeading(level) {
5774
6403
  const paras = selectedParagraphs(state);
5775
6404
  if (paras.length === 0) return false;
5776
6405
  if (dispatch) {
5777
- const allThisLevel = paras.every((p) => p.node.attrs["heading"] === level);
6406
+ const allThisLevel = paras.every(
6407
+ (p) => p.node.attrs["heading"] === level
6408
+ );
5778
6409
  const next = allThisLevel ? null : level;
5779
6410
  const tr = state.tr;
5780
6411
  for (const p of paras) tr.setNodeAttribute(p.pos, "heading", next);
@@ -6037,8 +6668,10 @@ var BapbongEditor = class {
6037
6668
  * Built from the shared command layer; the same ops run on a backend.
6038
6669
  * (Plugin-contributed commands are an additive follow-up.) */
6039
6670
  commands = defaultCommands();
6671
+ readClipboardFallback;
6040
6672
  constructor(stack, opts = {}) {
6041
6673
  this.stack = stack;
6674
+ this.readClipboardFallback = opts.readClipboardFallback;
6042
6675
  this.core = new RenderCore(stack, {
6043
6676
  viewport: opts.viewport,
6044
6677
  measureText: opts.measureText,
@@ -6055,9 +6688,12 @@ var BapbongEditor = class {
6055
6688
  stack.addEventListener("dblclick", this.onDblClick);
6056
6689
  stack.addEventListener("contextmenu", this.onContextMenu);
6057
6690
  window.addEventListener("keydown", this.onKeyDown, true);
6058
- this.plugins = new Collection([...createBuiltins(), ...opts.plugins ?? []], {
6059
- idProperty: "name"
6060
- });
6691
+ this.plugins = new Collection(
6692
+ [...createBuiltins(), ...opts.plugins ?? []],
6693
+ {
6694
+ idProperty: "name"
6695
+ }
6696
+ );
6061
6697
  this.pointerPlugins = [...this.plugins].some((p) => p.onPointer);
6062
6698
  this.pluginCtx = this.makePluginContext();
6063
6699
  for (const p of this.plugins) {
@@ -6082,8 +6718,14 @@ var BapbongEditor = class {
6082
6718
  setActionButton: (at, onActivate) => this.setActionButton(at, onActivate),
6083
6719
  setFrame: (frame) => this.setFrame(frame)
6084
6720
  };
6085
- Object.defineProperty(ctx, "state", { enumerable: true, get: () => this.state });
6086
- Object.defineProperty(ctx, "layout", { enumerable: true, get: () => this.core.layout });
6721
+ Object.defineProperty(ctx, "state", {
6722
+ enumerable: true,
6723
+ get: () => this.state
6724
+ });
6725
+ Object.defineProperty(ctx, "layout", {
6726
+ enumerable: true,
6727
+ get: () => this.core.layout
6728
+ });
6087
6729
  return ctx;
6088
6730
  }
6089
6731
  // ── Public API ──────────────────────────────────────────────────────
@@ -6226,34 +6868,116 @@ var BapbongEditor = class {
6226
6868
  this.bridge.focus();
6227
6869
  return document.execCommand("cut");
6228
6870
  }
6229
- /** Paste clipboard content (HTML parsed by the schema, else plain text). */
6871
+ /** Paste clipboard content HTML parsed by the schema, image blobs as
6872
+ * embedded images, else plain text.
6873
+ *
6874
+ * Menu-driven paste can't ride a native ClipboardEvent, so it climbs a
6875
+ * ladder of acquisition strategies until one yields content:
6876
+ * 1. the host's native clipboard reader (`readClipboardFallback`) when
6877
+ * provided — on WKWebView every programmatic clipboard READ (both
6878
+ * `navigator.clipboard.*` and execCommand) pops a "Paste" permission
6879
+ * callout and pends until the user taps it, so the desktop shell
6880
+ * must never touch the DOM clipboard from a menu click;
6881
+ * 2. async Clipboard API (`navigator.clipboard.read`) — browsers, where
6882
+ * a user-gesture read is granted silently;
6883
+ * 3. `document.execCommand('paste')` — legacy last resort. */
6230
6884
  async paste() {
6231
6885
  const view = this.bridge?.view;
6232
6886
  if (!view) return;
6887
+ this.bridge?.focus();
6888
+ if (await this.pasteViaHostClipboard(view)) return;
6889
+ if (this.readClipboardFallback) return;
6890
+ if (await this.pasteViaClipboardApi(view)) return;
6891
+ document.execCommand("paste");
6892
+ }
6893
+ async pasteViaClipboardApi(view) {
6233
6894
  try {
6234
6895
  const items = await navigator.clipboard.read();
6896
+ const htmlItem = items.find((i) => i.types.includes("text/html"));
6897
+ if (htmlItem) {
6898
+ const html = await (await htmlItem.getType("text/html")).text();
6899
+ const dom = new DOMParser().parseFromString(html, "text/html").body;
6900
+ if ((dom.textContent ?? "").trim()) {
6901
+ const slice = PMDOMParser.fromSchema(view.state.schema).parseSlice(
6902
+ dom
6903
+ );
6904
+ view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
6905
+ return true;
6906
+ }
6907
+ }
6235
6908
  for (const item of items) {
6236
- if (item.types.includes("text/html")) {
6237
- const html = await (await item.getType("text/html")).text();
6238
- const dom = document.createElement("div");
6239
- dom.innerHTML = html;
6240
- const slice = PMDOMParser.fromSchema(view.state.schema).parseSlice(dom);
6909
+ const type = item.types.find((t) => t.startsWith("image/"));
6910
+ if (type && await insertImageBlobs(view, [await item.getType(type)]))
6911
+ return true;
6912
+ }
6913
+ const text = await navigator.clipboard.readText();
6914
+ if (text) {
6915
+ view.dispatch(view.state.tr.insertText(text).scrollIntoView());
6916
+ return true;
6917
+ }
6918
+ return false;
6919
+ } catch (err) {
6920
+ console.warn(
6921
+ "[bapbong] Clipboard API paste unavailable, falling back:",
6922
+ err
6923
+ );
6924
+ return false;
6925
+ }
6926
+ }
6927
+ async pasteViaHostClipboard(view) {
6928
+ if (!this.readClipboardFallback) return false;
6929
+ try {
6930
+ const data = await this.readClipboardFallback();
6931
+ if (!data) return false;
6932
+ if (data.html) {
6933
+ const dom = new DOMParser().parseFromString(
6934
+ data.html,
6935
+ "text/html"
6936
+ ).body;
6937
+ if ((dom.textContent ?? "").trim()) {
6938
+ const slice = PMDOMParser.fromSchema(view.state.schema).parseSlice(
6939
+ dom
6940
+ );
6241
6941
  view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
6242
- return;
6942
+ return true;
6243
6943
  }
6244
6944
  }
6245
- await this.pasteText();
6246
- } catch {
6945
+ if (data.image?.length) {
6946
+ const blob = new Blob([data.image], {
6947
+ type: data.imageMime ?? "image/png"
6948
+ });
6949
+ if (await insertImageBlobs(view, [blob])) return true;
6950
+ }
6951
+ if (data.text) {
6952
+ view.dispatch(view.state.tr.insertText(data.text).scrollIntoView());
6953
+ return true;
6954
+ }
6955
+ return false;
6956
+ } catch (err) {
6957
+ console.warn("[bapbong] host clipboard fallback failed:", err);
6958
+ return false;
6247
6959
  }
6248
6960
  }
6249
6961
  /** Paste clipboard text as plain text (no formatting). */
6250
6962
  async pasteText() {
6251
6963
  const view = this.bridge?.view;
6252
6964
  if (!view) return;
6965
+ this.bridge?.focus();
6966
+ if (this.readClipboardFallback) {
6967
+ try {
6968
+ const data = await this.readClipboardFallback();
6969
+ if (data?.text)
6970
+ view.dispatch(view.state.tr.insertText(data.text).scrollIntoView());
6971
+ } catch (err) {
6972
+ console.warn("[bapbong] host clipboard fallback failed:", err);
6973
+ }
6974
+ return;
6975
+ }
6253
6976
  try {
6254
6977
  const text = await navigator.clipboard.readText();
6255
6978
  if (text) view.dispatch(view.state.tr.insertText(text).scrollIntoView());
6256
- } catch {
6979
+ } catch (err) {
6980
+ console.warn("[bapbong] Clipboard API readText unavailable:", err);
6257
6981
  }
6258
6982
  }
6259
6983
  destroy() {
@@ -6294,7 +7018,8 @@ var BapbongEditor = class {
6294
7018
  "Shift-ArrowUp": this.verticalCmd(-1, true),
6295
7019
  "Shift-ArrowDown": this.verticalCmd(1, true)
6296
7020
  },
6297
- onUpdate: (state, tr) => this.refresh(state, tr)
7021
+ onUpdate: (state, tr) => this.refresh(state, tr),
7022
+ handlePaste: imagePasteHandler
6298
7023
  });
6299
7024
  this.stack.appendChild(this.bridge.dom);
6300
7025
  this.render(this.bridge.state, true);
@@ -6323,14 +7048,25 @@ var BapbongEditor = class {
6323
7048
  else this.core.paintOverlay(overlay);
6324
7049
  const caret = this.lastCaret;
6325
7050
  if (caret && this.bridge) {
6326
- const pt = this.core.pageToCanvas({ pageIndex: caret.pageIndex, x: caret.x, y: caret.y });
7051
+ const pt = this.core.pageToCanvas({
7052
+ pageIndex: caret.pageIndex,
7053
+ x: caret.x,
7054
+ y: caret.y
7055
+ });
6327
7056
  if (pt) this.bridge.place(pt.x, pt.y, caret.height);
6328
7057
  }
6329
- this.emitChange({ state, pageCount: this.core.pageCount, docChanged: contentDirty });
7058
+ this.emitChange({
7059
+ state,
7060
+ pageCount: this.core.pageCount,
7061
+ docChanged: contentDirty
7062
+ });
6330
7063
  }
6331
7064
  /** The caret/selection overlay in the current blink phase. */
6332
7065
  currentOverlay() {
6333
- return { caret: this.caretVisible ? this.lastCaret : null, selection: this.lastSelection };
7066
+ return {
7067
+ caret: this.caretVisible ? this.lastCaret : null,
7068
+ selection: this.lastSelection
7069
+ };
6334
7070
  }
6335
7071
  /** Each plugin's doc-range decorations (the core resolves them to rects). */
6336
7072
  pluginDecorations() {
@@ -6405,7 +7141,8 @@ var BapbongEditor = class {
6405
7141
  * gesture), so plugins never steal keys from other inputs on the page. */
6406
7142
  onKeyDown = (ev) => {
6407
7143
  const target = ev.target;
6408
- if (target && target !== document.body && !this.stack.contains(target)) return;
7144
+ if (target && target !== document.body && !this.stack.contains(target))
7145
+ return;
6409
7146
  const offered = {
6410
7147
  key: ev.key,
6411
7148
  ctrlKey: ev.ctrlKey,
@@ -6439,7 +7176,10 @@ var BapbongEditor = class {
6439
7176
  this.lastCaret = this.core.caretRect(this.dragHead);
6440
7177
  this.lastSelection = from === to ? [] : this.core.selectionRects(from, to);
6441
7178
  this.caretVisible = true;
6442
- this.core.paintOverlay({ caret: this.lastCaret, selection: this.lastSelection });
7179
+ this.core.paintOverlay({
7180
+ caret: this.lastCaret,
7181
+ selection: this.lastSelection
7182
+ });
6443
7183
  }
6444
7184
  onPointerUp = (ev) => {
6445
7185
  if (this.offerPointer("up", ev)) {
@@ -6500,8 +7240,16 @@ var BapbongEditor = class {
6500
7240
  this.guideEl.style.cssText = "position:absolute;width:0;border-left:2px dashed #378add;pointer-events:none;z-index:5;";
6501
7241
  this.stack.appendChild(this.guideEl);
6502
7242
  }
6503
- const top = this.core.pageToCanvas({ pageIndex: guide.pageIndex, x: guide.x, y: guide.y });
6504
- const bottom = this.core.pageToCanvas({ pageIndex: guide.pageIndex, x: guide.x, y: guide.y + guide.height });
7243
+ const top = this.core.pageToCanvas({
7244
+ pageIndex: guide.pageIndex,
7245
+ x: guide.x,
7246
+ y: guide.y
7247
+ });
7248
+ const bottom = this.core.pageToCanvas({
7249
+ pageIndex: guide.pageIndex,
7250
+ x: guide.x,
7251
+ y: guide.y + guide.height
7252
+ });
6505
7253
  if (!top || !bottom) {
6506
7254
  this.guideEl.style.display = "none";
6507
7255
  return;
@@ -6549,7 +7297,11 @@ var BapbongEditor = class {
6549
7297
  this.stack.appendChild(el2);
6550
7298
  this.frameEl = el2;
6551
7299
  }
6552
- const tl = this.core.pageToCanvas({ pageIndex: frame.pageIndex, x: frame.x, y: frame.y });
7300
+ const tl = this.core.pageToCanvas({
7301
+ pageIndex: frame.pageIndex,
7302
+ x: frame.x,
7303
+ y: frame.y
7304
+ });
6553
7305
  const br = this.core.pageToCanvas({
6554
7306
  pageIndex: frame.pageIndex,
6555
7307
  x: frame.x + frame.width,
@@ -6585,7 +7337,11 @@ var BapbongEditor = class {
6585
7337
  this.highlightEls.forEach((el, i) => {
6586
7338
  const r = list[i];
6587
7339
  const tl = r && this.core.pageToCanvas({ pageIndex: r.pageIndex, x: r.x, y: r.y });
6588
- const br = r && this.core.pageToCanvas({ pageIndex: r.pageIndex, x: r.x + r.width, y: r.y + r.height });
7340
+ const br = r && this.core.pageToCanvas({
7341
+ pageIndex: r.pageIndex,
7342
+ x: r.x + r.width,
7343
+ y: r.y + r.height
7344
+ });
6589
7345
  if (!tl || !br) {
6590
7346
  el.style.display = "none";
6591
7347
  return;