react-word-editor 0.0.0-stage → 0.1.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.
@@ -0,0 +1,1887 @@
1
+ // src/toolbar/constants.js
2
+ var BLOCK_TYPES = [
3
+ { value: "paragraph", label: "Normal" },
4
+ { value: "h1", label: "Heading 1" },
5
+ { value: "h2", label: "Heading 2" },
6
+ { value: "h3", label: "Heading 3" },
7
+ { value: "h4", label: "Heading 4" }
8
+ ];
9
+ var DEFAULT_FONT_FAMILIES = [
10
+ { value: "", label: "Default font" },
11
+ { value: "Roboto, sans-serif", label: "Roboto" },
12
+ { value: "Arial, Helvetica, sans-serif", label: "Arial" },
13
+ { value: "Calibri, Carlito, sans-serif", label: "Calibri" },
14
+ { value: '"Times New Roman", Times, serif', label: "Times New Roman" },
15
+ { value: "Georgia, serif", label: "Georgia" },
16
+ { value: "Verdana, sans-serif", label: "Verdana" },
17
+ { value: '"Courier New", Courier, monospace', label: "Courier New" }
18
+ ];
19
+ var DEFAULT_FONT_SIZES = ["8", "9", "10", "11", "12", "14", "16", "18", "20", "24", "28", "36", "48"];
20
+ var LINE_HEIGHTS = [
21
+ { value: "", label: "Line spacing", menuLabel: "Default" },
22
+ { value: "1", label: "1.0" },
23
+ { value: "1.15", label: "1.15" },
24
+ { value: "1.5", label: "1.5" },
25
+ { value: "2", label: "2.0" },
26
+ { value: "2.5", label: "2.5" },
27
+ { value: "3", label: "3.0" }
28
+ ];
29
+ var COLOR_SWATCHES = [
30
+ "#000000",
31
+ "#434343",
32
+ "#666666",
33
+ "#999999",
34
+ "#cccccc",
35
+ "#ffffff",
36
+ "#c00000",
37
+ "#ff0000",
38
+ "#ffc000",
39
+ "#ffff00",
40
+ "#92d050",
41
+ "#00b050",
42
+ "#00b0f0",
43
+ "#0070c0",
44
+ "#002060",
45
+ "#7030a0",
46
+ "#2d5cf2",
47
+ "#f4b183",
48
+ "#fde9d9",
49
+ "#fff2cc",
50
+ "#e2efda",
51
+ "#ddebf7",
52
+ "#d9e2f3",
53
+ "#ededed"
54
+ ];
55
+ var DEFAULT_TOOLBAR = [
56
+ "undo",
57
+ "redo",
58
+ "|",
59
+ "blockType",
60
+ "fontFamily",
61
+ "fontSize",
62
+ "textCase",
63
+ "|",
64
+ "bold",
65
+ "italic",
66
+ "underline",
67
+ "strike",
68
+ "subscript",
69
+ "superscript",
70
+ "|",
71
+ "textColor",
72
+ "highlight",
73
+ "|",
74
+ "alignLeft",
75
+ "alignCenter",
76
+ "alignRight",
77
+ "alignJustify",
78
+ "|",
79
+ "bulletList",
80
+ "orderedList",
81
+ "outdent",
82
+ "indent",
83
+ "lineHeight",
84
+ "paragraphSpacing",
85
+ "|",
86
+ "table",
87
+ "image",
88
+ "link",
89
+ "horizontalRule",
90
+ "|",
91
+ "clearFormatting"
92
+ ];
93
+ var TABLE_PICKER_SIZE = { rows: 8, cols: 10 };
94
+ var PARAGRAPH_SPACING = [
95
+ { value: "default", label: "Default", before: null, after: null, lineHeight: null },
96
+ { value: "none", label: "No Paragraph Space", before: 0, after: 0, lineHeight: 1 },
97
+ { value: "compact", label: "Compact", before: 0, after: 4, lineHeight: 1 },
98
+ { value: "tight", label: "Tight", before: 0, after: 6, lineHeight: 1.15 },
99
+ { value: "open", label: "Open", before: 0, after: 10, lineHeight: 1.15 },
100
+ { value: "relaxed", label: "Relaxed", before: 0, after: 6, lineHeight: 1.5 },
101
+ { value: "double", label: "Double", before: 0, after: 8, lineHeight: 2 }
102
+ ];
103
+ var TEXT_CASES = [
104
+ { value: "sentence", label: "Sentence case." },
105
+ { value: "lower", label: "lowercase" },
106
+ { value: "upper", label: "UPPERCASE" },
107
+ { value: "capitalize", label: "Capitalise Each Word" },
108
+ { value: "toggle", label: "tOGGLE cASE" }
109
+ ];
110
+ var TABLE_BORDER_OPTIONS = [
111
+ { value: "bottom", label: "Bottom Border", lines: ["bottom"] },
112
+ { value: "top", label: "Top Border", lines: ["top"] },
113
+ { value: "left", label: "Left Border", lines: ["left"] },
114
+ { value: "right", label: "Right Border", lines: ["right"] },
115
+ "|",
116
+ { value: "none", label: "No Border", lines: [] },
117
+ { value: "all", label: "All Borders", lines: ["top", "bottom", "left", "right", "insideH", "insideV"] },
118
+ { value: "outside", label: "Outside Borders", lines: ["top", "bottom", "left", "right"] },
119
+ { value: "inside", label: "Inside Borders", lines: ["insideH", "insideV"] },
120
+ "|",
121
+ { value: "insideH", label: "Inside Horizontal Border", lines: ["insideH"] },
122
+ { value: "insideV", label: "Inside Vertical Border", lines: ["insideV"] }
123
+ ];
124
+
125
+ // src/extensions/paragraphFormat.js
126
+ import { Extension } from "@tiptap/core";
127
+
128
+ // src/utils/units.js
129
+ var PX_PER_UNIT = {
130
+ px: 1,
131
+ pt: 96 / 72,
132
+ pc: 16,
133
+ in: 96,
134
+ cm: 96 / 2.54,
135
+ mm: 96 / 25.4
136
+ };
137
+ var LENGTH_RE = /^(-?\d*\.?\d+)\s*(px|pt|pc|in|cm|mm|em|rem)?$/i;
138
+ var toPx = (value, fontSizePx = 16) => {
139
+ if (value === null || value === void 0 || value === "") return null;
140
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
141
+ const match = String(value).trim().match(LENGTH_RE);
142
+ if (!match) return null;
143
+ const num = parseFloat(match[1]);
144
+ const unit = (match[2] || "px").toLowerCase();
145
+ if (unit === "em" || unit === "rem") return num * fontSizePx;
146
+ return num * PX_PER_UNIT[unit];
147
+ };
148
+ var round = (n, digits = 2) => {
149
+ const f = 10 ** digits;
150
+ return Math.round(n * f) / f;
151
+ };
152
+ var pxToPt = (px) => round(px * 0.75);
153
+ var toPt = (value, fontSizePx) => {
154
+ const px = toPx(value, fontSizePx);
155
+ return px === null ? null : pxToPt(px);
156
+ };
157
+ var parseLineHeight = (value) => {
158
+ if (value === null || value === void 0 || value === "") return null;
159
+ const str = String(value).trim().toLowerCase();
160
+ if (str === "normal" || str === "inherit" || str === "initial") return null;
161
+ if (str.endsWith("%")) {
162
+ const pct2 = parseFloat(str);
163
+ return Number.isFinite(pct2) && pct2 > 0 ? round(pct2 / 100) : null;
164
+ }
165
+ const num = Number(str);
166
+ if (Number.isFinite(num) && num > 0) return round(num);
167
+ return null;
168
+ };
169
+
170
+ // src/extensions/paragraphFormat.js
171
+ var INDENT_STEP_PX = 48;
172
+ var MAX_INDENT_PX = INDENT_STEP_PX * 12;
173
+ var BLOCK_TYPES2 = ["paragraph", "heading"];
174
+ var pxAttribute = (cssProp, styleKey, { allowNegative = false, keepZero = false } = {}) => ({
175
+ default: null,
176
+ keepOnSplit: true,
177
+ parseHTML: (el) => {
178
+ var _a;
179
+ const px = toPx((_a = el.style) == null ? void 0 : _a[styleKey]);
180
+ if (px === null || !allowNegative && px < 0) return null;
181
+ if (px === 0 && !keepZero) return null;
182
+ return round(px);
183
+ },
184
+ renderHTML: (attrs) => {
185
+ const value = attrs[styleKey];
186
+ if (value === null || value === void 0) return {};
187
+ return { style: `${cssProp}: ${value}px` };
188
+ }
189
+ });
190
+ var updateBlocks = (tr, state, fn) => {
191
+ const { from, to } = state.selection;
192
+ let changed = false;
193
+ state.doc.nodesBetween(from, to, (node, pos) => {
194
+ if (!BLOCK_TYPES2.includes(node.type.name)) return true;
195
+ const next = fn(node.attrs);
196
+ if (next) {
197
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, ...next });
198
+ changed = true;
199
+ }
200
+ return false;
201
+ });
202
+ return changed;
203
+ };
204
+ var ptToPx = (pt) => pt === null ? null : round(toPx(`${pt}pt`));
205
+ var spacingAttrs = (preset) => ({
206
+ marginTop: preset.before ? ptToPx(preset.before) : null,
207
+ marginBottom: ptToPx(preset.after),
208
+ lineHeight: preset.lineHeight
209
+ });
210
+ var matchSpacingPreset = (attrs = {}) => {
211
+ var _a;
212
+ return ((_a = PARAGRAPH_SPACING.find((preset) => {
213
+ const want = spacingAttrs(preset);
214
+ return Object.keys(want).every((k) => {
215
+ var _a2;
216
+ return ((_a2 = attrs[k]) != null ? _a2 : null) === want[k];
217
+ });
218
+ })) == null ? void 0 : _a.value) || null;
219
+ };
220
+ var ParagraphFormat = Extension.create({
221
+ name: "paragraphFormat",
222
+ priority: 50,
223
+ addGlobalAttributes() {
224
+ return [
225
+ {
226
+ types: BLOCK_TYPES2,
227
+ attributes: {
228
+ marginLeft: {
229
+ ...pxAttribute("margin-left", "marginLeft"),
230
+ // TinyMCE indents with padding-left; treat both as the indent.
231
+ parseHTML: (el) => {
232
+ var _a, _b;
233
+ const px = (toPx((_a = el.style) == null ? void 0 : _a.marginLeft) || 0) + (toPx((_b = el.style) == null ? void 0 : _b.paddingLeft) || 0);
234
+ return px > 0 ? round(px) : null;
235
+ }
236
+ },
237
+ textIndent: pxAttribute("text-indent", "textIndent", { allowNegative: true }),
238
+ marginTop: pxAttribute("margin-top", "marginTop"),
239
+ marginBottom: pxAttribute("margin-bottom", "marginBottom", { keepZero: true }),
240
+ lineHeight: {
241
+ default: null,
242
+ keepOnSplit: true,
243
+ parseHTML: (el) => {
244
+ var _a;
245
+ return parseLineHeight((_a = el.style) == null ? void 0 : _a.lineHeight);
246
+ },
247
+ renderHTML: (attrs) => attrs.lineHeight ? { style: `line-height: ${attrs.lineHeight}` } : {}
248
+ }
249
+ }
250
+ }
251
+ ];
252
+ },
253
+ addCommands() {
254
+ return {
255
+ indent: () => ({ editor, tr, state, dispatch }) => {
256
+ if (editor.isActive("listItem")) return editor.commands.sinkListItem("listItem");
257
+ const changed = updateBlocks(tr, state, (attrs) => {
258
+ const next = Math.min((attrs.marginLeft || 0) + INDENT_STEP_PX, MAX_INDENT_PX);
259
+ return next === attrs.marginLeft ? null : { marginLeft: next };
260
+ });
261
+ if (changed && dispatch) dispatch(tr);
262
+ return changed;
263
+ },
264
+ outdent: () => ({ editor, tr, state, dispatch }) => {
265
+ if (editor.isActive("listItem")) return editor.commands.liftListItem("listItem");
266
+ const changed = updateBlocks(tr, state, (attrs) => {
267
+ if (!attrs.marginLeft && !attrs.textIndent) return null;
268
+ if (!attrs.marginLeft) return { textIndent: null };
269
+ const next = Math.max(attrs.marginLeft - INDENT_STEP_PX, 0);
270
+ return { marginLeft: next || null };
271
+ });
272
+ if (changed && dispatch) dispatch(tr);
273
+ return changed;
274
+ },
275
+ setLineHeight: (lineHeight) => ({ tr, state, dispatch }) => {
276
+ const value = parseLineHeight(lineHeight);
277
+ const changed = updateBlocks(tr, state, () => ({ lineHeight: value }));
278
+ if (changed && dispatch) dispatch(tr);
279
+ return changed;
280
+ },
281
+ setParagraphSpacing: (value) => ({ tr, state, dispatch }) => {
282
+ const preset = PARAGRAPH_SPACING.find((p) => p.value === value);
283
+ if (!preset) return false;
284
+ const changed = updateBlocks(tr, state, () => spacingAttrs(preset));
285
+ if (changed && dispatch) dispatch(tr);
286
+ return changed;
287
+ },
288
+ unsetParagraphFormat: () => ({ tr, state, dispatch }) => {
289
+ const changed = updateBlocks(tr, state, () => ({
290
+ marginLeft: null,
291
+ textIndent: null,
292
+ marginTop: null,
293
+ marginBottom: null,
294
+ lineHeight: null
295
+ }));
296
+ if (changed && dispatch) dispatch(tr);
297
+ return changed;
298
+ }
299
+ };
300
+ },
301
+ addKeyboardShortcuts() {
302
+ return {
303
+ Tab: () => {
304
+ if (this.editor.isActive("table")) return false;
305
+ this.editor.commands.indent();
306
+ return true;
307
+ },
308
+ "Shift-Tab": () => {
309
+ if (this.editor.isActive("table")) return false;
310
+ this.editor.commands.outdent();
311
+ return true;
312
+ }
313
+ };
314
+ }
315
+ });
316
+
317
+ // src/extensions/tableCells.js
318
+ import { TableCell, TableHeader } from "@tiptap/extension-table";
319
+
320
+ // src/utils/borders.js
321
+ var SIDES = ["top", "right", "bottom", "left"];
322
+ var STYLES = ["none", "hidden", "solid", "dashed", "dotted", "double", "groove", "ridge", "inset", "outset"];
323
+ var WIDTH_WORDS = { thin: "1px", medium: "3px", thick: "5px" };
324
+ var splitTokens = (value) => value.match(/(rgba?\([^)]*\)|[^\s]+)/g) || [];
325
+ var expandBox = (values) => {
326
+ const [a, b = a, c = a, d = b] = values;
327
+ return [a, b, c, d];
328
+ };
329
+ var parseShorthand = (value) => {
330
+ const out = {};
331
+ splitTokens(value.trim()).forEach((token) => {
332
+ const t = token.toLowerCase();
333
+ if (STYLES.includes(t)) out.style = t;
334
+ else if (WIDTH_WORDS[t] || toPx(t) !== null) out.width = WIDTH_WORDS[t] || t;
335
+ else out.color = token;
336
+ });
337
+ return out;
338
+ };
339
+ var parseBorders = (styleAttr) => {
340
+ const sides = Object.fromEntries(SIDES.map((s) => [s, null]));
341
+ const set = (side, part) => {
342
+ sides[side] = { ...sides[side] || {}, ...part };
343
+ };
344
+ (styleAttr || "").split(";").forEach((decl) => {
345
+ const idx = decl.indexOf(":");
346
+ if (idx < 1) return;
347
+ const prop = decl.slice(0, idx).trim().toLowerCase();
348
+ const value = decl.slice(idx + 1).trim().replace(/\s*!important$/i, "");
349
+ if (!prop.startsWith("border") || !value) return;
350
+ if (prop === "border") {
351
+ const part = parseShorthand(value);
352
+ SIDES.forEach((s) => {
353
+ sides[s] = { width: "3px", style: "none", color: "currentcolor", ...part };
354
+ });
355
+ return;
356
+ }
357
+ const m = prop.match(/^border-(top|right|bottom|left)(?:-(width|style|color))?$/);
358
+ if (m) {
359
+ if (!m[2]) {
360
+ sides[m[1]] = { width: "3px", style: "none", color: "currentcolor", ...parseShorthand(value) };
361
+ } else set(m[1], { [m[2]]: value });
362
+ return;
363
+ }
364
+ const all = prop.match(/^border-(width|style|color)$/);
365
+ if (all) {
366
+ expandBox(splitTokens(value)).forEach((v, i) => set(SIDES[i], { [all[1]]: v }));
367
+ }
368
+ });
369
+ return sides;
370
+ };
371
+ var formatBorder = (side) => {
372
+ if (!side) return null;
373
+ const style = (side.style || "none").toLowerCase();
374
+ if (style === "none" || style === "hidden") return "none";
375
+ const px = toPx(WIDTH_WORDS[side.width] || side.width || "1px");
376
+ if (px === 0) return "none";
377
+ const w = px === null ? 1 : Math.max(round(px), 0.5);
378
+ const color = !side.color || side.color === "currentcolor" ? "#000000" : side.color;
379
+ return `${w}px ${style} ${color}`;
380
+ };
381
+
382
+ // src/extensions/tableCells.js
383
+ var SIDES2 = ["Top", "Right", "Bottom", "Left"];
384
+ var DEFAULT_BORDER = "1px solid #000000";
385
+ var rgbToHex = (color) => {
386
+ const m = String(color || "").match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)$/i);
387
+ if (!m) return color;
388
+ if (m[4] !== void 0 && parseFloat(m[4]) === 0) return "transparent";
389
+ return `#${[m[1], m[2], m[3]].map((n) => Number(n).toString(16).padStart(2, "0")).join("")}`;
390
+ };
391
+ var parseBorderSide = (el, side) => formatBorder(parseBorders(el.getAttribute("style"))[side.toLowerCase()]);
392
+ var parseColwidth = (el) => {
393
+ var _a;
394
+ const attr = el.getAttribute("colwidth");
395
+ if (attr) return attr.split(",").map((w) => parseInt(w, 10));
396
+ const raw = el.getAttribute("data-width") || ((_a = el.style) == null ? void 0 : _a.width) || el.getAttribute("width");
397
+ const px = toPx(raw && /^\d+(\.\d+)?$/.test(raw) ? `${raw}px` : raw);
398
+ if (!px) return null;
399
+ const span = parseInt(el.getAttribute("colspan") || "1", 10) || 1;
400
+ return Array.from({ length: span }, () => Math.max(Math.round(px / span), 20));
401
+ };
402
+ var cellAttributes = (parent) => ({
403
+ ...parent,
404
+ colwidth: {
405
+ default: null,
406
+ parseHTML: parseColwidth,
407
+ renderHTML: (attrs) => attrs.colwidth ? { colwidth: attrs.colwidth.join(",") } : {}
408
+ },
409
+ backgroundColor: {
410
+ default: null,
411
+ parseHTML: (el) => {
412
+ var _a;
413
+ const bg = ((_a = el.style) == null ? void 0 : _a.backgroundColor) || el.getAttribute("bgcolor");
414
+ const hex = bg ? rgbToHex(bg) : null;
415
+ return hex && hex !== "transparent" ? hex : null;
416
+ },
417
+ renderHTML: (attrs) => attrs.backgroundColor ? { style: `background-color: ${attrs.backgroundColor}` } : {}
418
+ },
419
+ verticalAlign: {
420
+ default: null,
421
+ parseHTML: (el) => {
422
+ var _a;
423
+ const v = (((_a = el.style) == null ? void 0 : _a.verticalAlign) || el.getAttribute("valign") || "").toLowerCase();
424
+ return ["top", "middle", "bottom"].includes(v) ? v : null;
425
+ },
426
+ renderHTML: (attrs) => attrs.verticalAlign ? { style: `vertical-align: ${attrs.verticalAlign}` } : {}
427
+ },
428
+ ...Object.fromEntries(
429
+ SIDES2.map((side) => [
430
+ `border${side}`,
431
+ {
432
+ default: null,
433
+ parseHTML: (el) => parseBorderSide(el, side),
434
+ renderHTML: (attrs) => {
435
+ const value = attrs[`border${side}`];
436
+ return value ? { style: `border-${side.toLowerCase()}: ${value}` } : {};
437
+ }
438
+ }
439
+ ])
440
+ )
441
+ });
442
+ var RichTableCell = TableCell.extend({
443
+ addAttributes() {
444
+ var _a;
445
+ return cellAttributes((_a = this.parent) == null ? void 0 : _a.call(this));
446
+ }
447
+ });
448
+ var RichTableHeader = TableHeader.extend({
449
+ addAttributes() {
450
+ var _a;
451
+ return cellAttributes((_a = this.parent) == null ? void 0 : _a.call(this));
452
+ }
453
+ });
454
+
455
+ // src/extensions/nodeIds.js
456
+ import { Extension as Extension2 } from "@tiptap/core";
457
+
458
+ // src/utils/meta.js
459
+ var SILENT_META = "flabsEditorSilent";
460
+
461
+ // src/extensions/nodeIds.js
462
+ var NODE_ID_ATTRIBUTE = "data-node-id";
463
+ var NODE_ID_TYPES = [
464
+ "paragraph",
465
+ "heading",
466
+ "listItem",
467
+ "bulletList",
468
+ "orderedList",
469
+ "table",
470
+ "image",
471
+ "horizontalRule",
472
+ "blockquote"
473
+ ];
474
+ var newId = () => typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `n-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
475
+ var ensureNodeIds = (editor) => {
476
+ var _a, _b;
477
+ if (!editor || editor.isDestroyed || !((_b = (_a = editor.view) == null ? void 0 : _a.dom) == null ? void 0 : _b.isConnected)) return false;
478
+ const { state } = editor;
479
+ const seen = /* @__PURE__ */ new Set();
480
+ const tr = state.tr;
481
+ state.doc.descendants((node, pos) => {
482
+ if (!NODE_ID_TYPES.includes(node.type.name)) return true;
483
+ const id = node.attrs.nodeId;
484
+ if (!id || seen.has(id)) {
485
+ const fresh = newId();
486
+ seen.add(fresh);
487
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, nodeId: fresh });
488
+ } else {
489
+ seen.add(id);
490
+ }
491
+ return true;
492
+ });
493
+ if (!tr.docChanged) return false;
494
+ tr.setMeta("addToHistory", false);
495
+ tr.setMeta(SILENT_META, true);
496
+ editor.view.dispatch(tr);
497
+ return true;
498
+ };
499
+ var NodeIds = Extension2.create({
500
+ name: "nodeIds",
501
+ addGlobalAttributes() {
502
+ return [
503
+ {
504
+ types: NODE_ID_TYPES,
505
+ attributes: {
506
+ nodeId: {
507
+ default: null,
508
+ keepOnSplit: false,
509
+ parseHTML: (el) => el.getAttribute(NODE_ID_ATTRIBUTE),
510
+ renderHTML: (attrs) => attrs.nodeId ? { [NODE_ID_ATTRIBUTE]: attrs.nodeId } : {}
511
+ }
512
+ }
513
+ }
514
+ ];
515
+ },
516
+ onBlur() {
517
+ ensureNodeIds(this.editor);
518
+ }
519
+ });
520
+
521
+ // src/extensions/index.js
522
+ import StarterKit from "@tiptap/starter-kit";
523
+ import { Table, TableRow } from "@tiptap/extension-table";
524
+ import {
525
+ BackgroundColor,
526
+ Color,
527
+ FontFamily,
528
+ FontSize,
529
+ TextStyle
530
+ } from "@tiptap/extension-text-style";
531
+ import TextAlign from "@tiptap/extension-text-align";
532
+ import Image from "@tiptap/extension-image";
533
+ import Subscript from "@tiptap/extension-subscript";
534
+ import Superscript from "@tiptap/extension-superscript";
535
+ import { Placeholder } from "@tiptap/extensions";
536
+
537
+ // src/extensions/imageUpload.js
538
+ import { Extension as Extension3 } from "@tiptap/core";
539
+ import { Plugin, PluginKey } from "@tiptap/pm/state";
540
+ var readAsDataUrl = (file) => new Promise((resolve, reject) => {
541
+ const reader = new FileReader();
542
+ reader.onload = () => resolve(reader.result);
543
+ reader.onerror = () => reject(new Error("Could not read the image"));
544
+ reader.readAsDataURL(file);
545
+ });
546
+ var imageFiles = (list) => Array.from(list || []).filter((f) => {
547
+ var _a;
548
+ return (_a = f == null ? void 0 : f.type) == null ? void 0 : _a.startsWith("image/");
549
+ });
550
+ var uploadImage = async (file, { onImageUpload, maxImageSize }) => {
551
+ if (maxImageSize && file.size > maxImageSize) {
552
+ const mb = Math.round(maxImageSize / 1024 / 1024 * 10) / 10;
553
+ throw new Error(`Image size must be less than or equal to ${mb}MB`);
554
+ }
555
+ if (onImageUpload) return onImageUpload(file);
556
+ return readAsDataUrl(file);
557
+ };
558
+ var insertImages = async (editor, files, options, pos) => {
559
+ var _a;
560
+ for (const file of files) {
561
+ try {
562
+ const src = await uploadImage(file, options);
563
+ if (!src || editor.isDestroyed) continue;
564
+ const chain = editor.chain().focus();
565
+ if (typeof pos === "number") chain.insertContentAt(pos, { type: "image", attrs: { src } });
566
+ else chain.setImage({ src });
567
+ chain.run();
568
+ } catch (err) {
569
+ (_a = options.onError) == null ? void 0 : _a.call(options, err);
570
+ }
571
+ }
572
+ };
573
+ var ImageUpload = Extension3.create({
574
+ name: "imageUpload",
575
+ addOptions() {
576
+ return { onImageUpload: null, maxImageSize: null, onError: null };
577
+ },
578
+ addProseMirrorPlugins() {
579
+ const { editor, options } = this;
580
+ return [
581
+ new Plugin({
582
+ key: new PluginKey("flabsImageUpload"),
583
+ props: {
584
+ handlePaste: (view, event) => {
585
+ var _a;
586
+ const data = event.clipboardData;
587
+ const files = imageFiles(data == null ? void 0 : data.files);
588
+ if (!files.length) return false;
589
+ const html = data.getData("text/html");
590
+ if (html && /<(p|table|span|div|li|h\d)[\s>]/i.test(html)) return false;
591
+ if ((_a = data.getData("text/plain")) == null ? void 0 : _a.trim()) return false;
592
+ insertImages(editor, files, options);
593
+ return true;
594
+ },
595
+ handleDrop: (view, event) => {
596
+ var _a;
597
+ const files = imageFiles((_a = event.dataTransfer) == null ? void 0 : _a.files);
598
+ if (!files.length) return false;
599
+ event.preventDefault();
600
+ const coords = view.posAtCoords({ left: event.clientX, top: event.clientY });
601
+ insertImages(editor, files, options, coords == null ? void 0 : coords.pos);
602
+ return true;
603
+ }
604
+ }
605
+ })
606
+ ];
607
+ }
608
+ });
609
+
610
+ // src/extensions/textCase.js
611
+ import { Extension as Extension4 } from "@tiptap/core";
612
+ import { TextSelection } from "@tiptap/pm/state";
613
+
614
+ // src/utils/textCase.js
615
+ var LETTER = /\p{L}/u;
616
+ var WORD_CHAR = /[\p{L}\p{N}'’]/u;
617
+ var SENTENCE_END = /[.!?]/;
618
+ var swapCase = (ch) => {
619
+ const upper = ch.toUpperCase();
620
+ return upper === ch ? ch.toLowerCase() : upper;
621
+ };
622
+ var createCaser = (mode, before = "") => {
623
+ let sentenceStart = true;
624
+ let afterEnd = false;
625
+ let inWord = false;
626
+ const step = (ch) => {
627
+ let out = ch;
628
+ if (mode === "lower") out = ch.toLowerCase();
629
+ else if (mode === "upper") out = ch.toUpperCase();
630
+ else if (mode === "toggle") out = swapCase(ch);
631
+ else if (mode === "capitalize") {
632
+ if (LETTER.test(ch)) out = inWord ? ch.toLowerCase() : ch.toUpperCase();
633
+ } else if (mode === "sentence" && LETTER.test(ch)) {
634
+ out = sentenceStart ? ch.toUpperCase() : ch.toLowerCase();
635
+ }
636
+ inWord = WORD_CHAR.test(ch);
637
+ if (LETTER.test(ch) || /\p{N}/u.test(ch)) {
638
+ sentenceStart = false;
639
+ afterEnd = false;
640
+ } else if (SENTENCE_END.test(ch)) afterEnd = true;
641
+ else if (/\s/.test(ch)) {
642
+ if (afterEnd) sentenceStart = true;
643
+ } else afterEnd = false;
644
+ return out;
645
+ };
646
+ Array.from(before).forEach(step);
647
+ return (text) => Array.from(text).map(step).join("");
648
+ };
649
+ var TEXT_CASE_MODES = ["sentence", "lower", "upper", "capitalize", "toggle"];
650
+
651
+ // src/extensions/textCase.js
652
+ var WORD_CHAR2 = /[\p{L}\p{N}'’]/u;
653
+ var LEAF = "\n";
654
+ var wordAround = ($pos) => {
655
+ const { parent } = $pos;
656
+ if (!parent.isTextblock) return null;
657
+ const text = parent.textBetween(0, parent.content.size, void 0, LEAF);
658
+ let start = $pos.parentOffset;
659
+ let end = start;
660
+ while (start > 0 && WORD_CHAR2.test(text[start - 1])) start -= 1;
661
+ while (end < text.length && WORD_CHAR2.test(text[end])) end += 1;
662
+ if (start === end) return null;
663
+ const base = $pos.start();
664
+ return { from: base + start, to: base + end };
665
+ };
666
+ var collectRuns = (doc, from, to) => {
667
+ const blocks = [];
668
+ doc.nodesBetween(from, to, (node, pos) => {
669
+ if (!node.isTextblock) return true;
670
+ const runs = [];
671
+ node.forEach((child, offset) => {
672
+ if (!child.isText) return;
673
+ const start = Math.max(from, pos + 1 + offset);
674
+ const end = Math.min(to, pos + 1 + offset + child.nodeSize);
675
+ if (start < end) runs.push({ start, end, marks: child.marks });
676
+ });
677
+ if (runs.length) {
678
+ const before = node.textBetween(0, runs[0].start - pos - 1, void 0, LEAF);
679
+ blocks.push({ before, runs });
680
+ }
681
+ return false;
682
+ });
683
+ return blocks;
684
+ };
685
+ var TextCase = Extension4.create({
686
+ name: "textCase",
687
+ addCommands() {
688
+ return {
689
+ setTextCase: (mode) => ({ state, tr, dispatch }) => {
690
+ if (!TEXT_CASE_MODES.includes(mode)) return false;
691
+ const { selection } = state;
692
+ const range = selection.empty ? wordAround(selection.$from) : { from: selection.from, to: selection.to };
693
+ if (!range) return false;
694
+ const edits = [];
695
+ collectRuns(state.doc, range.from, range.to).forEach(({ before, runs }) => {
696
+ const caser = createCaser(mode, before);
697
+ runs.forEach(({ start, end, marks }) => {
698
+ const text = state.doc.textBetween(start, end);
699
+ const next = caser(text);
700
+ if (next !== text) edits.push({ start, end, next, marks });
701
+ });
702
+ });
703
+ if (!edits.length) return false;
704
+ if (!dispatch) return true;
705
+ edits.reverse().forEach(({ start, end, next, marks }) => {
706
+ tr.replaceWith(start, end, state.schema.text(next, marks));
707
+ });
708
+ const sameSize = tr.doc.content.size === state.doc.content.size;
709
+ if (selection instanceof TextSelection) {
710
+ const from = sameSize ? selection.from : tr.mapping.map(selection.from, -1);
711
+ const to = sameSize ? selection.to : tr.mapping.map(selection.to, 1);
712
+ tr.setSelection(TextSelection.create(tr.doc, from, to));
713
+ } else tr.setSelection(selection.map(tr.doc, tr.mapping));
714
+ dispatch(tr);
715
+ return true;
716
+ }
717
+ };
718
+ }
719
+ });
720
+
721
+ // src/extensions/tableBorders.js
722
+ import { Extension as Extension5 } from "@tiptap/core";
723
+ import { isInTable, selectedRect } from "@tiptap/pm/tables";
724
+ var KINDS = ["top", "bottom", "left", "right", "insideH", "insideV"];
725
+ var BORDER_MODES = {
726
+ top: ["top"],
727
+ bottom: ["bottom"],
728
+ left: ["left"],
729
+ right: ["right"],
730
+ insideH: ["insideH"],
731
+ insideV: ["insideV"],
732
+ outside: ["top", "bottom", "left", "right"],
733
+ inside: ["insideH", "insideV"],
734
+ all: KINDS,
735
+ none: KINDS
736
+ };
737
+ var APPLY_ONLY = ["all", "none"];
738
+ var collectEdges = (state) => {
739
+ if (!isInTable(state)) return null;
740
+ const rect = selectedRect(state);
741
+ const { map, tableStart } = rect;
742
+ const at = (row, col) => row < 0 || col < 0 || row >= map.height || col >= map.width ? null : tableStart + map.map[row * map.width + col];
743
+ const edges = Object.fromEntries(KINDS.map((k) => [k, []]));
744
+ const add = (kind, pairs) => {
745
+ const members = pairs.filter(([pos]) => pos !== null).map(([pos, attr]) => ({ pos, attr }));
746
+ if (members.length) edges[kind].push(members);
747
+ };
748
+ for (let row = rect.top; row <= rect.bottom; row += 1) {
749
+ for (let col = rect.left; col < rect.right; col += 1) {
750
+ const above = at(row - 1, col);
751
+ const below = at(row, col);
752
+ if (above !== null && above === below) continue;
753
+ const kind = row === rect.top ? "top" : row === rect.bottom ? "bottom" : "insideH";
754
+ add(kind, [[above, "borderBottom"], [below, "borderTop"]]);
755
+ }
756
+ }
757
+ for (let col = rect.left; col <= rect.right; col += 1) {
758
+ for (let row = rect.top; row < rect.bottom; row += 1) {
759
+ const left = at(row, col - 1);
760
+ const right = at(row, col);
761
+ if (left !== null && left === right) continue;
762
+ const kind = col === rect.left ? "left" : col === rect.right ? "right" : "insideV";
763
+ add(kind, [[left, "borderRight"], [right, "borderLeft"]]);
764
+ }
765
+ }
766
+ return edges;
767
+ };
768
+ var sideOf = (doc, { pos, attr }) => {
769
+ var _a, _b, _c;
770
+ return (_c = (_b = (_a = doc.nodeAt(pos)) == null ? void 0 : _a.attrs) == null ? void 0 : _b[attr]) != null ? _c : null;
771
+ };
772
+ var isVisible = (doc, members) => members.some((m) => sideOf(doc, m) !== "none");
773
+ var kindOn = (doc, list) => list.length > 0 && list.every((members) => isVisible(doc, members));
774
+ var getTableBorderState = (state) => {
775
+ const edges = collectEdges(state);
776
+ if (!edges) return null;
777
+ const on = Object.fromEntries(KINDS.map((k) => [k, kindOn(state.doc, edges[k])]));
778
+ const exists = Object.fromEntries(KINDS.map((k) => [k, edges[k].length > 0]));
779
+ const present = KINDS.filter((k) => exists[k]);
780
+ const allOff = KINDS.every((k) => edges[k].every((members) => !isVisible(state.doc, members)));
781
+ const modeOn = (mode) => {
782
+ if (mode === "none") return allOff;
783
+ const kinds = BORDER_MODES[mode].filter((k) => exists[k]);
784
+ return kinds.length > 0 && kinds.every((k) => on[k]);
785
+ };
786
+ return {
787
+ ...Object.fromEntries(Object.keys(BORDER_MODES).map((m) => [m, modeOn(m)])),
788
+ exists,
789
+ hasInside: present.includes("insideH") || present.includes("insideV")
790
+ };
791
+ };
792
+ var edgeStyle = (doc, members) => {
793
+ const explicit = members.map((m) => sideOf(doc, m)).find((v) => v && v !== "none");
794
+ return explicit || DEFAULT_BORDER;
795
+ };
796
+ var TableBorders = Extension5.create({
797
+ name: "tableBorders",
798
+ addCommands() {
799
+ return {
800
+ setTableBorders: (mode) => ({ state, tr, dispatch }) => {
801
+ if (!BORDER_MODES[mode]) return false;
802
+ const edges = collectEdges(state);
803
+ if (!edges) return false;
804
+ const kinds = BORDER_MODES[mode].filter((k) => edges[k].length);
805
+ if (!kinds.length) return false;
806
+ const lists = kinds.flatMap((k) => edges[k]);
807
+ const turnOn = mode === "none" ? false : APPLY_ONLY.includes(mode) || !kinds.every((k) => kindOn(state.doc, edges[k]));
808
+ const updates = /* @__PURE__ */ new Map();
809
+ lists.forEach((members) => {
810
+ const value = turnOn ? edgeStyle(state.doc, members) : "none";
811
+ members.forEach(({ pos, attr }) => {
812
+ updates.set(pos, { ...updates.get(pos) || {}, [attr]: value });
813
+ });
814
+ });
815
+ if (!dispatch) return true;
816
+ updates.forEach((attrs, pos) => {
817
+ const node = tr.doc.nodeAt(pos);
818
+ if (node) tr.setNodeMarkup(pos, void 0, { ...node.attrs, ...attrs });
819
+ });
820
+ dispatch(tr);
821
+ return true;
822
+ }
823
+ };
824
+ }
825
+ });
826
+
827
+ // src/extensions/index.js
828
+ var sizeAttr = (key) => ({
829
+ default: null,
830
+ parseHTML: (el) => {
831
+ var _a;
832
+ const raw = el.getAttribute(key) || ((_a = el.style) == null ? void 0 : _a[key]);
833
+ const px = toPx(raw && /^\d+(\.\d+)?$/.test(raw) ? `${raw}px` : raw);
834
+ return px ? Math.round(px) : null;
835
+ },
836
+ renderHTML: (attrs) => attrs[key] ? { [key]: attrs[key] } : {}
837
+ });
838
+ var RichImage = Image.extend({
839
+ addAttributes() {
840
+ var _a;
841
+ return {
842
+ ...(_a = this.parent) == null ? void 0 : _a.call(this),
843
+ width: sizeAttr("width"),
844
+ height: sizeAttr("height")
845
+ };
846
+ }
847
+ });
848
+ var buildSchemaExtensions = ({ nodeIds = false } = {}) => {
849
+ const list = [
850
+ StarterKit.configure({
851
+ codeBlock: false,
852
+ heading: { levels: [1, 2, 3, 4] },
853
+ link: { openOnClick: false, autolink: true, defaultProtocol: "https" },
854
+ undoRedo: { depth: 200 }
855
+ }),
856
+ TextStyle,
857
+ Color,
858
+ BackgroundColor,
859
+ FontFamily,
860
+ FontSize,
861
+ TextAlign.configure({ types: ["heading", "paragraph"] }),
862
+ Subscript,
863
+ Superscript,
864
+ RichImage.configure({ allowBase64: true, inline: false }),
865
+ Table.configure({ resizable: true, allowTableNodeSelection: true }),
866
+ TableRow,
867
+ RichTableHeader,
868
+ RichTableCell,
869
+ ParagraphFormat
870
+ ];
871
+ if (nodeIds) list.push(NodeIds);
872
+ return list;
873
+ };
874
+ var buildEditorExtensions = ({
875
+ nodeIds,
876
+ placeholder,
877
+ onImageUpload,
878
+ maxImageSize,
879
+ onError
880
+ } = {}) => [
881
+ ...buildSchemaExtensions({ nodeIds }),
882
+ ImageUpload.configure({ onImageUpload, maxImageSize, onError }),
883
+ TextCase,
884
+ TableBorders,
885
+ ...placeholder ? [Placeholder.configure({ placeholder })] : []
886
+ ];
887
+
888
+ // src/paste/styleSheet.js
889
+ var RULE_RE = /([^{}@]+)\{([^{}]*)\}/g;
890
+ var stripComments = (css) => css.replace(/\/\*[\s\S]*?\*\//g, "");
891
+ var removeAtRules = (css) => {
892
+ let out = "";
893
+ let i = 0;
894
+ while (i < css.length) {
895
+ const at = css.indexOf("@", i);
896
+ if (at < 0) {
897
+ out += css.slice(i);
898
+ break;
899
+ }
900
+ out += css.slice(i, at);
901
+ let depth = 0;
902
+ let j = at;
903
+ for (; j < css.length; j += 1) {
904
+ const ch = css[j];
905
+ if (ch === ";" && depth === 0) break;
906
+ if (ch === "{") depth += 1;
907
+ if (ch === "}") {
908
+ depth -= 1;
909
+ if (depth === 0) break;
910
+ }
911
+ }
912
+ i = j + 1;
913
+ }
914
+ return out;
915
+ };
916
+ var parseStyleRules = (cssText) => {
917
+ const css = removeAtRules(stripComments(cssText || "").replace(/<!--|-->/g, ""));
918
+ const rules = [];
919
+ let match;
920
+ RULE_RE.lastIndex = 0;
921
+ while (match = RULE_RE.exec(css)) {
922
+ const declarations = match[2].trim();
923
+ if (!declarations) continue;
924
+ match[1].split(",").map((s) => s.trim()).filter(Boolean).forEach((selector) => rules.push({ selector, declarations }));
925
+ }
926
+ return rules;
927
+ };
928
+ var inlineStyleRules = (doc) => {
929
+ const styleEls = Array.from(doc.querySelectorAll("style"));
930
+ if (!styleEls.length) return;
931
+ const rules = styleEls.flatMap((el) => parseStyleRules(el.textContent));
932
+ const collected = /* @__PURE__ */ new Map();
933
+ rules.forEach(({ selector, declarations }) => {
934
+ if (/[:]{1,2}[a-z-]+/i.test(selector.replace(/\\:/g, ""))) return;
935
+ let nodes;
936
+ try {
937
+ nodes = doc.body.querySelectorAll(selector);
938
+ } catch (e) {
939
+ return;
940
+ }
941
+ nodes.forEach((node) => {
942
+ collected.set(node, `${collected.get(node) || ""}${declarations};`);
943
+ });
944
+ });
945
+ collected.forEach((declarations, node) => {
946
+ const own = node.getAttribute("style") || "";
947
+ node.setAttribute("style", `${declarations}${own}`);
948
+ });
949
+ styleEls.forEach((el) => el.remove());
950
+ };
951
+
952
+ // src/paste/wordLists.js
953
+ var MSO_LIST_RE = /mso-list\s*:\s*(l\d+)\s+level(\d+)/i;
954
+ var ORDERED_MARKER_RE = /^\(?([0-9]+|[a-z]|[ivxlcdm]+)[.)]$/i;
955
+ var getStyleAttr = (el) => el.getAttribute("style") || "";
956
+ var isIgnoreSpan = (el) => el.nodeType === 1 && /mso-list\s*:\s*ignore/i.test(getStyleAttr(el));
957
+ var takeMarker = (p) => {
958
+ const ignore = Array.from(p.querySelectorAll("span")).find(isIgnoreSpan);
959
+ if (!ignore) return "";
960
+ const text = (ignore.textContent || "").replace(/ /g, " ").trim();
961
+ let target = ignore;
962
+ while (target.parentElement && target.parentElement !== p && (target.parentElement.textContent || "").trim() === (ignore.textContent || "").trim()) {
963
+ target = target.parentElement;
964
+ }
965
+ target.remove();
966
+ return text;
967
+ };
968
+ var listTypeFor = (marker) => {
969
+ const m = marker.match(ORDERED_MARKER_RE);
970
+ if (!m) return { tag: "ul" };
971
+ const token = m[1];
972
+ if (/^\d+$/.test(token)) return { tag: "ol", type: "1", start: parseInt(token, 10) };
973
+ if (/^[ivxlcdm]+$/.test(token) && token.length > 1) {
974
+ return { tag: "ol", type: token === token.toLowerCase() ? "i" : "I" };
975
+ }
976
+ if (/^[a-z]$/.test(token)) return { tag: "ol", type: "a", start: token.charCodeAt(0) - 96 };
977
+ return { tag: "ol", type: "A", start: token.charCodeAt(0) - 64 };
978
+ };
979
+ var LIST_STYLE_PROPS = ["margin-left", "text-indent", "margin"];
980
+ var cleanListParagraphStyle = (p) => {
981
+ const kept = getStyleAttr(p).split(";").filter((decl) => {
982
+ var _a;
983
+ const prop = (_a = decl.split(":")[0]) == null ? void 0 : _a.trim().toLowerCase();
984
+ return prop && !LIST_STYLE_PROPS.includes(prop) && !prop.startsWith("mso-");
985
+ });
986
+ if (kept.length) p.setAttribute("style", kept.join(";"));
987
+ else p.removeAttribute("style");
988
+ };
989
+ var isListParagraph = (el) => (el == null ? void 0 : el.nodeType) === 1 && el.tagName === "P" && MSO_LIST_RE.test(getStyleAttr(el));
990
+ var buildList = (doc, items) => {
991
+ const root = { level: 0, el: null, children: [] };
992
+ const stack = [];
993
+ items.forEach(({ level, info, p }) => {
994
+ while (stack.length && stack[stack.length - 1].level > level) stack.pop();
995
+ let current = stack[stack.length - 1];
996
+ if (!current || current.level < level) {
997
+ const listEl = doc.createElement(info.tag);
998
+ if (info.type && info.type !== "1") listEl.setAttribute("type", info.type);
999
+ if (info.start && info.start !== 1) listEl.setAttribute("start", String(info.start));
1000
+ const parentLi = current == null ? void 0 : current.lastLi;
1001
+ if (parentLi) parentLi.appendChild(listEl);
1002
+ else root.children.push(listEl);
1003
+ current = { level, el: listEl, lastLi: null };
1004
+ stack.push(current);
1005
+ }
1006
+ const li = doc.createElement("li");
1007
+ cleanListParagraphStyle(p);
1008
+ li.appendChild(p);
1009
+ current.el.appendChild(li);
1010
+ current.lastLi = li;
1011
+ });
1012
+ return root.children;
1013
+ };
1014
+ var convertWordLists = (doc, container = doc.body) => {
1015
+ var _a;
1016
+ const children = Array.from(container.children);
1017
+ let i = 0;
1018
+ while (i < children.length) {
1019
+ const el = children[i];
1020
+ if (!isListParagraph(el)) {
1021
+ if (((_a = el.children) == null ? void 0 : _a.length) && !["P", "UL", "OL"].includes(el.tagName)) {
1022
+ convertWordLists(doc, el);
1023
+ }
1024
+ i += 1;
1025
+ continue;
1026
+ }
1027
+ const items = [];
1028
+ const anchor = el;
1029
+ let j = i;
1030
+ let listId = null;
1031
+ while (j < children.length && isListParagraph(children[j])) {
1032
+ const p = children[j];
1033
+ const [, id, levelStr] = getStyleAttr(p).match(MSO_LIST_RE);
1034
+ if (listId && id !== listId && parseInt(levelStr, 10) === 1) break;
1035
+ listId = listId || id;
1036
+ const marker = takeMarker(p);
1037
+ items.push({ level: parseInt(levelStr, 10), info: listTypeFor(marker), p });
1038
+ j += 1;
1039
+ }
1040
+ const placeholder = doc.createElement("span");
1041
+ container.insertBefore(placeholder, anchor);
1042
+ buildList(doc, items).forEach((list) => container.insertBefore(list, placeholder));
1043
+ placeholder.remove();
1044
+ i = j;
1045
+ }
1046
+ };
1047
+
1048
+ // src/paste/cleanHtml.js
1049
+ var REMOVE_TAGS = ["meta", "link", "script", "title", "xml", "head", "object", "embed", "iframe"];
1050
+ var DROP_STYLE_PROPS = [
1051
+ "tab-stops",
1052
+ "layout-grid-mode",
1053
+ "text-autospace",
1054
+ "text-justify",
1055
+ "punctuation-wrap",
1056
+ "word-break",
1057
+ "page-break-before",
1058
+ "page-break-after",
1059
+ "page-break-inside",
1060
+ "break-before",
1061
+ "break-after",
1062
+ "font-variant-ligatures",
1063
+ "font-variant-caps",
1064
+ "font-variant-east-asian",
1065
+ "font-variant-numeric",
1066
+ "orphans",
1067
+ "widows",
1068
+ "white-space",
1069
+ "caret-color",
1070
+ "text-decoration-thickness",
1071
+ "text-decoration-style",
1072
+ "text-decoration-color",
1073
+ "-webkit-text-stroke-width",
1074
+ "letter-spacing",
1075
+ "word-spacing"
1076
+ ];
1077
+ var CELL_TEXT_PROPS = ["color", "font-weight", "font-style", "font-size", "font-family", "text-decoration"];
1078
+ var isWordHtml = (html) => /urn:schemas-microsoft-com:office|class="?Mso|mso-[a-z-]+\s*:|<o:p>|ProgId"? content="?(Word|Excel)/i.test(
1079
+ html || ""
1080
+ );
1081
+ var filterDeclarations = (style) => style.split(";").map((d) => d.trim()).filter(Boolean).filter((decl) => {
1082
+ const idx = decl.indexOf(":");
1083
+ if (idx < 1) return false;
1084
+ const prop = decl.slice(0, idx).trim().toLowerCase();
1085
+ if (prop.startsWith("mso-") || prop.startsWith("-ms-")) return false;
1086
+ return !DROP_STYLE_PROPS.includes(prop);
1087
+ }).map(
1088
+ (decl) => decl.replace(/\bwindowtext\b/gi, "#000000").replace(/(\d)\.0+(?=[a-z%])/gi, "$1").replace(/\bauto\b(?=\s*$)/i, "")
1089
+ ).filter((decl) => !/:\s*$/.test(decl));
1090
+ var cleanStyleAttr = (el) => {
1091
+ const style = el.getAttribute("style");
1092
+ if (style === null) return;
1093
+ const kept = filterDeclarations(style);
1094
+ if (kept.length) el.setAttribute("style", kept.join("; "));
1095
+ else el.removeAttribute("style");
1096
+ };
1097
+ var unwrap = (el) => {
1098
+ const parent = el.parentNode;
1099
+ if (!parent) return;
1100
+ while (el.firstChild) parent.insertBefore(el.firstChild, el);
1101
+ parent.removeChild(el);
1102
+ };
1103
+ var removeComments = (root) => {
1104
+ const walker = root.ownerDocument.createTreeWalker(root, 128);
1105
+ const comments = [];
1106
+ while (walker.nextNode()) comments.push(walker.currentNode);
1107
+ comments.forEach((c) => c.remove());
1108
+ };
1109
+ var isNamespaced = (el) => el.tagName.includes(":");
1110
+ var appendStyle = (el, decl) => {
1111
+ const own = el.getAttribute("style");
1112
+ el.setAttribute("style", own ? `${own.replace(/;?\s*$/, "")}; ${decl}` : decl);
1113
+ };
1114
+ var moveAlignAttr = (el) => {
1115
+ const align = el.getAttribute("align");
1116
+ if (!align) return;
1117
+ if (!/text-align/i.test(el.getAttribute("style") || "")) {
1118
+ appendStyle(el, `text-align: ${align.toLowerCase()}`);
1119
+ }
1120
+ el.removeAttribute("align");
1121
+ };
1122
+ var convertFontTag = (doc, el) => {
1123
+ const span = doc.createElement("span");
1124
+ const decls = [];
1125
+ if (el.getAttribute("color")) decls.push(`color: ${el.getAttribute("color")}`);
1126
+ if (el.getAttribute("face")) decls.push(`font-family: ${el.getAttribute("face")}`);
1127
+ const own = el.getAttribute("style");
1128
+ if (own) decls.push(own);
1129
+ if (decls.length) span.setAttribute("style", decls.join("; "));
1130
+ while (el.firstChild) span.appendChild(el.firstChild);
1131
+ el.replaceWith(span);
1132
+ };
1133
+ var wrapCellText = (doc, cell) => {
1134
+ const style = cell.getAttribute("style") || "";
1135
+ const moved = [];
1136
+ const kept = [];
1137
+ style.split(";").map((d) => d.trim()).filter(Boolean).forEach((decl) => {
1138
+ const prop = decl.split(":")[0].trim().toLowerCase();
1139
+ (CELL_TEXT_PROPS.includes(prop) ? moved : kept).push(decl);
1140
+ });
1141
+ if (!moved.length) return;
1142
+ const span = doc.createElement("span");
1143
+ span.setAttribute("style", moved.join("; "));
1144
+ const blocks = cell.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li");
1145
+ if (blocks.length) {
1146
+ blocks.forEach((block) => {
1147
+ const inner = span.cloneNode(false);
1148
+ while (block.firstChild) inner.appendChild(block.firstChild);
1149
+ block.appendChild(inner);
1150
+ });
1151
+ } else {
1152
+ while (cell.firstChild) span.appendChild(cell.firstChild);
1153
+ cell.appendChild(span);
1154
+ }
1155
+ if (kept.length) cell.setAttribute("style", kept.join("; "));
1156
+ else cell.removeAttribute("style");
1157
+ };
1158
+ var cellWidthFromStyle = (cell) => {
1159
+ var _a;
1160
+ if (cell.getAttribute("colwidth")) return;
1161
+ const width = cell.getAttribute("width");
1162
+ const styleWidth = (_a = (cell.getAttribute("style") || "").match(/(?:^|;)\s*width\s*:\s*([^;]+)/i)) == null ? void 0 : _a[1];
1163
+ const value = styleWidth || (width && /^\d+(\.\d+)?$/.test(width) ? `${width}px` : null);
1164
+ if (value) cell.setAttribute("data-width", value.trim());
1165
+ };
1166
+ var isEmptyInline = (el) => !el.attributes.length || el.attributes.length === 1 && el.hasAttribute("lang");
1167
+ var cleanElement = (doc, el, word) => {
1168
+ const tag = el.tagName.toLowerCase();
1169
+ if (tag === "font") return convertFontTag(doc, el);
1170
+ if (isNamespaced(el)) {
1171
+ if (tag === "o:p" && !el.textContent) return el.remove();
1172
+ return unwrap(el);
1173
+ }
1174
+ if (tag === "img") {
1175
+ const src = el.getAttribute("src") || "";
1176
+ if (/^(file|cid|blob):/i.test(src) || !src) return el.remove();
1177
+ }
1178
+ if (word && el.hasAttribute("v:shapes")) el.removeAttribute("v:shapes");
1179
+ ["p", "div", "td", "th", "h1", "h2", "h3", "h4", "h5", "h6"].includes(tag) && moveAlignAttr(el);
1180
+ if (tag === "td" || tag === "th") cellWidthFromStyle(el);
1181
+ cleanStyleAttr(el);
1182
+ if (tag === "td" || tag === "th") wrapCellText(doc, el);
1183
+ if (word) {
1184
+ el.removeAttribute("class");
1185
+ el.removeAttribute("lang");
1186
+ }
1187
+ if (tag === "b" && /docs-internal-guid/.test(el.id || "")) return unwrap(el);
1188
+ if (tag === "span" && isEmptyInline(el)) return unwrap(el);
1189
+ return void 0;
1190
+ };
1191
+ var walk = (doc, root, word) => {
1192
+ Array.from(root.children).forEach((child) => {
1193
+ walk(doc, child, word);
1194
+ cleanElement(doc, child, word);
1195
+ });
1196
+ };
1197
+ var applyBorderlessTables = (doc) => {
1198
+ doc.body.querySelectorAll("table").forEach((table) => {
1199
+ const attr = table.getAttribute("border");
1200
+ const style = (table.getAttribute("style") || "").toLowerCase();
1201
+ const noBorder = attr === "0" || /(^|;)\s*border\s*:\s*(none|0(px)?)\s*(;|$)/.test(style);
1202
+ if (!noBorder) return;
1203
+ Array.from(table.rows || []).forEach(
1204
+ (row) => Array.from(row.cells || []).forEach((cell) => {
1205
+ if (!/border/i.test(cell.getAttribute("style") || "")) appendStyle(cell, "border: none");
1206
+ })
1207
+ );
1208
+ });
1209
+ };
1210
+ var parse = (html) => new DOMParser().parseFromString(html, "text/html");
1211
+ var cleanHtml = (html) => {
1212
+ if (!html || typeof html !== "string") return html || "";
1213
+ if (typeof DOMParser === "undefined") return html;
1214
+ const word = isWordHtml(html);
1215
+ const fragmentMatch = html.match(/<!--StartFragment-->([\s\S]*?)<!--EndFragment-->/);
1216
+ const doc = parse(html);
1217
+ if (fragmentMatch && doc.querySelectorAll("style").length === 0) {
1218
+ doc.body.innerHTML = fragmentMatch[1];
1219
+ }
1220
+ inlineStyleRules(doc);
1221
+ if (!word) applyBorderlessTables(doc);
1222
+ if (word) convertWordLists(doc);
1223
+ removeComments(doc.body);
1224
+ REMOVE_TAGS.forEach((t) => doc.body.querySelectorAll(t).forEach((el) => el.remove()));
1225
+ walk(doc, doc.body, word);
1226
+ return doc.body.innerHTML;
1227
+ };
1228
+
1229
+ // src/utils/listMarkers.js
1230
+ var BULLETS = ["\u2022", "\u25E6", "\u25AA"];
1231
+ var toAlpha = (n) => {
1232
+ let s = "";
1233
+ let x = n;
1234
+ while (x > 0) {
1235
+ const r = (x - 1) % 26;
1236
+ s = String.fromCharCode(97 + r) + s;
1237
+ x = Math.floor((x - 1) / 26);
1238
+ }
1239
+ return s;
1240
+ };
1241
+ var ROMAN = [
1242
+ [1e3, "m"],
1243
+ [900, "cm"],
1244
+ [500, "d"],
1245
+ [400, "cd"],
1246
+ [100, "c"],
1247
+ [90, "xc"],
1248
+ [50, "l"],
1249
+ [40, "xl"],
1250
+ [10, "x"],
1251
+ [9, "ix"],
1252
+ [5, "v"],
1253
+ [4, "iv"],
1254
+ [1, "i"]
1255
+ ];
1256
+ var toRoman = (n) => {
1257
+ let x = n;
1258
+ return ROMAN.reduce((acc, [v, s]) => {
1259
+ let out = acc;
1260
+ while (x >= v) {
1261
+ out += s;
1262
+ x -= v;
1263
+ }
1264
+ return out;
1265
+ }, "");
1266
+ };
1267
+ var formatNumber = (n, type) => {
1268
+ switch (type) {
1269
+ case "a":
1270
+ return toAlpha(n);
1271
+ case "A":
1272
+ return toAlpha(n).toUpperCase();
1273
+ case "i":
1274
+ return toRoman(n);
1275
+ case "I":
1276
+ return toRoman(n).toUpperCase();
1277
+ default:
1278
+ return String(n);
1279
+ }
1280
+ };
1281
+ var listMarker = (listNode, index, depth = 0, bullets = BULLETS) => {
1282
+ var _a, _b, _c;
1283
+ if (listNode.type === "orderedList") {
1284
+ const start = (_b = (_a = listNode.attrs) == null ? void 0 : _a.start) != null ? _b : 1;
1285
+ return `${formatNumber(start + index, (_c = listNode.attrs) == null ? void 0 : _c.type)}.`;
1286
+ }
1287
+ return bullets[depth % bullets.length];
1288
+ };
1289
+
1290
+ // src/output/text.js
1291
+ var inlineText = (node) => {
1292
+ if (node.type === "text") return node.text || "";
1293
+ if (node.type === "hardBreak") return "\n";
1294
+ return (node.content || []).map(inlineText).join("");
1295
+ };
1296
+ var blockToLines = (node, depth = 0) => {
1297
+ const pad = " ".repeat(depth);
1298
+ switch (node.type) {
1299
+ case "paragraph":
1300
+ case "heading":
1301
+ return [inlineText(node)];
1302
+ case "bulletList":
1303
+ case "orderedList":
1304
+ return (node.content || []).flatMap((item, index) => {
1305
+ const marker = listMarker(node, index, depth);
1306
+ const [first = "", ...rest] = (item.content || []).flatMap(
1307
+ (child) => blockToLines(child, child.type.endsWith("List") ? depth + 1 : depth)
1308
+ );
1309
+ return [`${pad}${marker} ${first}`, ...rest];
1310
+ });
1311
+ case "table":
1312
+ return (node.content || []).map(
1313
+ (row) => (row.content || []).map(
1314
+ (cell) => (cell.content || []).flatMap((c) => blockToLines(c, 0)).join(" ").trim()
1315
+ ).join(" ")
1316
+ );
1317
+ case "blockquote":
1318
+ return (node.content || []).flatMap((c) => blockToLines(c, depth));
1319
+ case "horizontalRule":
1320
+ return ["---"];
1321
+ case "image":
1322
+ return [];
1323
+ default:
1324
+ return node.content ? node.content.flatMap((c) => blockToLines(c, depth)) : [];
1325
+ }
1326
+ };
1327
+ var jsonToText = (json) => ((json == null ? void 0 : json.content) || []).flatMap((node) => blockToLines(node)).join("\n").replace(/ /g, " ");
1328
+
1329
+ // src/pdf/options.js
1330
+ var STANDARD_FONTS = [
1331
+ "Helvetica",
1332
+ "Helvetica-Bold",
1333
+ "Helvetica-Oblique",
1334
+ "Helvetica-BoldOblique",
1335
+ "Times-Roman",
1336
+ "Times-Bold",
1337
+ "Times-Italic",
1338
+ "Times-BoldItalic",
1339
+ "Courier",
1340
+ "Courier-Bold",
1341
+ "Courier-Oblique",
1342
+ "Courier-BoldOblique"
1343
+ ];
1344
+ var DEFAULT_PDF_OPTIONS = {
1345
+ // Base text size in pt; headings scale from it. Only set on the output
1346
+ // when passed explicitly, otherwise the surrounding react-pdf font size wins.
1347
+ fontSize: 10,
1348
+ // Drop font sizes stored in the content (like the old removeFontSize helper).
1349
+ ignoreFontSizes: false,
1350
+ fontFamily: null,
1351
+ color: null,
1352
+ lineHeight: null,
1353
+ // react-pdf throws on fonts that are not registered, so a CSS font-family
1354
+ // is only kept when it is in `fonts` (registered names) or `fontMap`.
1355
+ fonts: [],
1356
+ fontMap: {},
1357
+ // Unknown families fall back to inheriting the surrounding font.
1358
+ monospaceFont: "Courier",
1359
+ paragraphSpacing: 4,
1360
+ listItemSpacing: 1,
1361
+ headingScale: { 1: 2, 2: 1.6, 3: 1.3, 4: 1.15 },
1362
+ headingSpacing: { top: 6, bottom: 4 },
1363
+ listIndent: 14,
1364
+ bullets: ["\u2022", "\u2022", "\u2022"],
1365
+ tableBorder: "0.75pt solid #000000",
1366
+ cellPadding: { vertical: 2, horizontal: 4 },
1367
+ linkColor: "#2d5cf2",
1368
+ // Keep table rows on one page.
1369
+ keepRowsTogether: true,
1370
+ // Per-node style overrides merged last, e.g. { paragraph: { color: '#333' } }.
1371
+ styles: {}
1372
+ };
1373
+ var resolveOptions = (options = {}) => ({
1374
+ ...DEFAULT_PDF_OPTIONS,
1375
+ ...options,
1376
+ explicitFontSize: options.fontSize !== void 0 && options.fontSize !== null,
1377
+ headingScale: { ...DEFAULT_PDF_OPTIONS.headingScale, ...options.headingScale || {} },
1378
+ headingSpacing: { ...DEFAULT_PDF_OPTIONS.headingSpacing, ...options.headingSpacing || {} },
1379
+ cellPadding: { ...DEFAULT_PDF_OPTIONS.cellPadding, ...options.cellPadding || {} },
1380
+ styles: options.styles || {}
1381
+ });
1382
+ var cleanFamily = (name) => name.trim().replace(/^["']|["']$/g, "").trim();
1383
+ var resolveFontFamily = (cssValue, opts) => {
1384
+ if (!cssValue) return null;
1385
+ const allowed = new Map(
1386
+ [...opts.fonts || [], ...STANDARD_FONTS].map((f) => [f.toLowerCase(), f])
1387
+ );
1388
+ const map = Object.fromEntries(
1389
+ Object.entries(opts.fontMap || {}).map(([k, v]) => [k.toLowerCase(), v])
1390
+ );
1391
+ const candidates = String(cssValue).split(",").map(cleanFamily).filter(Boolean);
1392
+ for (const name of candidates) {
1393
+ const key = name.toLowerCase();
1394
+ if (map[key]) return map[key];
1395
+ if (allowed.has(key)) return allowed.get(key);
1396
+ }
1397
+ return null;
1398
+ };
1399
+
1400
+ // src/utils/colors.js
1401
+ var NAMED = {
1402
+ black: "#000000",
1403
+ white: "#ffffff",
1404
+ red: "#ff0000",
1405
+ green: "#008000",
1406
+ blue: "#0000ff",
1407
+ yellow: "#ffff00",
1408
+ gray: "#808080",
1409
+ grey: "#808080",
1410
+ silver: "#c0c0c0",
1411
+ maroon: "#800000",
1412
+ navy: "#000080",
1413
+ teal: "#008080",
1414
+ olive: "#808000",
1415
+ purple: "#800080",
1416
+ orange: "#ffa500",
1417
+ lime: "#00ff00",
1418
+ aqua: "#00ffff",
1419
+ cyan: "#00ffff",
1420
+ fuchsia: "#ff00ff",
1421
+ magenta: "#ff00ff"
1422
+ };
1423
+ var hex2 = (n) => Math.max(0, Math.min(255, Math.round(Number(n)))).toString(16).padStart(2, "0");
1424
+ var toHexColor = (value) => {
1425
+ if (!value) return null;
1426
+ const str = String(value).trim().toLowerCase();
1427
+ if (!str || str === "transparent" || str === "inherit" || str === "initial" || str === "auto") return null;
1428
+ if (/^#[0-9a-f]{6}$/.test(str)) return str;
1429
+ if (/^#[0-9a-f]{3}$/.test(str)) return `#${str[1]}${str[1]}${str[2]}${str[2]}${str[3]}${str[3]}`;
1430
+ if (/^#[0-9a-f]{8}$/.test(str)) return str.slice(0, 7);
1431
+ const m = str.match(/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)(?:[\s,/]+([\d.]+%?))?\s*\)$/);
1432
+ if (m) {
1433
+ if (m[4] !== void 0 && parseFloat(m[4]) === 0) return null;
1434
+ return `#${hex2(m[1])}${hex2(m[2])}${hex2(m[3])}`;
1435
+ }
1436
+ if (str === "windowtext" || str === "currentcolor") return "#000000";
1437
+ return NAMED[str] || null;
1438
+ };
1439
+
1440
+ // src/pdf/inline.js
1441
+ var SCRIPT_RATIO = 0.7;
1442
+ var decorations = (marks) => {
1443
+ const parts = [];
1444
+ if (marks.some((m) => m.type === "underline" || m.type === "link")) parts.push("underline");
1445
+ if (marks.some((m) => m.type === "strike")) parts.push("line-through");
1446
+ return parts.length ? parts.join(" ") : null;
1447
+ };
1448
+ var markStyle = (marks = [], ctx, opts) => {
1449
+ const style = {};
1450
+ let fontSize = null;
1451
+ marks.forEach((mark) => {
1452
+ const a = mark.attrs || {};
1453
+ switch (mark.type) {
1454
+ case "bold":
1455
+ style.fontWeight = "bold";
1456
+ break;
1457
+ case "italic":
1458
+ style.fontStyle = "italic";
1459
+ break;
1460
+ case "code":
1461
+ style.fontFamily = opts.monospaceFont;
1462
+ break;
1463
+ case "link":
1464
+ style.color = opts.linkColor;
1465
+ break;
1466
+ case "textStyle": {
1467
+ const color = toHexColor(a.color);
1468
+ if (color) style.color = color;
1469
+ const bg = toHexColor(a.backgroundColor);
1470
+ if (bg) style.backgroundColor = bg;
1471
+ const pt = toPt(a.fontSize, ctx.fontSize * 4 / 3);
1472
+ if (pt && !opts.ignoreFontSizes) fontSize = pt;
1473
+ const family = resolveFontFamily(a.fontFamily, opts);
1474
+ if (family) style.fontFamily = family;
1475
+ break;
1476
+ }
1477
+ default:
1478
+ break;
1479
+ }
1480
+ });
1481
+ const textDecoration = decorations(marks);
1482
+ if (textDecoration) style.textDecoration = textDecoration;
1483
+ const script = marks.find((m) => m.type === "subscript" || m.type === "superscript");
1484
+ if (script) {
1485
+ style.verticalAlign = script.type === "subscript" ? "sub" : "super";
1486
+ fontSize = (fontSize || ctx.fontSize) * SCRIPT_RATIO;
1487
+ }
1488
+ if (fontSize) style.fontSize = Math.round(fontSize * 100) / 100;
1489
+ return style;
1490
+ };
1491
+ var isEmptyStyle = (style) => !Object.keys(style).length;
1492
+ var convertInline = (nodes = [], ctx, opts) => {
1493
+ const out = [];
1494
+ nodes.forEach((node) => {
1495
+ var _a, _b;
1496
+ if (node.type === "hardBreak") {
1497
+ out.push("\n");
1498
+ return;
1499
+ }
1500
+ if (node.type !== "text" || !node.text) return;
1501
+ const style = markStyle(node.marks, ctx, opts);
1502
+ const textNode = isEmptyStyle(style) ? node.text : { type: "text", style: { ...style, ...opts.styles.text || {} }, children: [node.text] };
1503
+ const link = (_a = node.marks) == null ? void 0 : _a.find((m) => m.type === "link");
1504
+ if ((_b = link == null ? void 0 : link.attrs) == null ? void 0 : _b.href) {
1505
+ out.push({
1506
+ type: "link",
1507
+ src: link.attrs.href,
1508
+ style: { ...opts.styles.link || {} },
1509
+ children: [textNode]
1510
+ });
1511
+ } else {
1512
+ out.push(textNode);
1513
+ }
1514
+ });
1515
+ return out;
1516
+ };
1517
+
1518
+ // src/pdf/table.js
1519
+ var PDF_BORDER_STYLES = ["solid", "dashed", "dotted"];
1520
+ var borderToPdf = (value) => {
1521
+ if (!value || value === "none") return null;
1522
+ const side = parseBorders(`border-top: ${value}`).top;
1523
+ const formatted = formatBorder(side);
1524
+ if (!formatted || formatted === "none") return null;
1525
+ const [width, style] = formatted.split(" ");
1526
+ const pt = Math.max(pxToPt(toPx(width) || 1), 0.25);
1527
+ const color = toHexColor(side.color) || "#000000";
1528
+ return `${pt}pt ${PDF_BORDER_STYLES.includes(style) ? style : "solid"} ${color}`;
1529
+ };
1530
+ var buildGrid = (rows) => {
1531
+ const occupied = [];
1532
+ const placements = [];
1533
+ let numCols = 0;
1534
+ rows.forEach((row, r) => {
1535
+ occupied[r] = occupied[r] || [];
1536
+ let c = 0;
1537
+ (row.content || []).forEach((cell) => {
1538
+ var _a, _b;
1539
+ while (occupied[r][c]) c += 1;
1540
+ const colspan = ((_a = cell.attrs) == null ? void 0 : _a.colspan) || 1;
1541
+ const rowspan = ((_b = cell.attrs) == null ? void 0 : _b.rowspan) || 1;
1542
+ const placement = { cell, r, c, colspan, rowspan };
1543
+ placements.push(placement);
1544
+ for (let dr = 0; dr < rowspan; dr += 1) {
1545
+ occupied[r + dr] = occupied[r + dr] || [];
1546
+ for (let dc = 0; dc < colspan; dc += 1) occupied[r + dr][c + dc] = placement;
1547
+ }
1548
+ c += colspan;
1549
+ numCols = Math.max(numCols, c);
1550
+ });
1551
+ });
1552
+ return { occupied: occupied.slice(0, rows.length), placements, numCols };
1553
+ };
1554
+ var columnWidths = (placements, numCols) => {
1555
+ const widths = Array(numCols).fill(null);
1556
+ placements.forEach(({ cell, c, colspan }) => {
1557
+ var _a;
1558
+ const cw = (_a = cell.attrs) == null ? void 0 : _a.colwidth;
1559
+ if (!Array.isArray(cw)) return;
1560
+ for (let i = 0; i < colspan; i += 1) {
1561
+ if (widths[c + i] === null && cw[i]) widths[c + i] = cw[i];
1562
+ }
1563
+ });
1564
+ const known = widths.filter((w) => w !== null);
1565
+ const complete = known.length === numCols;
1566
+ const fallback = known.length ? known.reduce((a, b) => a + b, 0) / known.length : 100;
1567
+ const filled = widths.map((w) => w === null ? fallback : w);
1568
+ const total = filled.reduce((a, b) => a + b, 0) || 1;
1569
+ return {
1570
+ percents: filled.map((w) => w / total * 100),
1571
+ totalPt: complete ? pxToPt(total) : null
1572
+ };
1573
+ };
1574
+ var VALIGN = { middle: "center", bottom: "flex-end", top: "flex-start" };
1575
+ var resolveEdge = (own, other, hasNeighbour, opts) => {
1576
+ const visible = [own, other].find((v) => v && v !== "none");
1577
+ if (visible) return borderToPdf(visible);
1578
+ if (!own || hasNeighbour && !other) return opts.tableBorder;
1579
+ return null;
1580
+ };
1581
+ var cellBorders = (p, r, c, occupied, opts) => {
1582
+ var _a, _b, _c, _d;
1583
+ const attrs = p.cell.attrs || {};
1584
+ const right = (_a = occupied[r]) == null ? void 0 : _a[c + p.colspan];
1585
+ const below = (_b = occupied[p.r + p.rowspan]) == null ? void 0 : _b[c];
1586
+ const style = {
1587
+ borderTop: p.r === 0 ? resolveEdge(attrs.borderTop, null, false, opts) : null,
1588
+ borderLeft: c === 0 ? resolveEdge(attrs.borderLeft, null, false, opts) : null,
1589
+ borderRight: resolveEdge(attrs.borderRight, (_c = right == null ? void 0 : right.cell.attrs) == null ? void 0 : _c.borderLeft, !!right, opts),
1590
+ borderBottom: resolveEdge(attrs.borderBottom, (_d = below == null ? void 0 : below.cell.attrs) == null ? void 0 : _d.borderTop, !!below, opts)
1591
+ };
1592
+ return Object.fromEntries(Object.entries(style).filter(([, v]) => v));
1593
+ };
1594
+ var pct = (n) => `${round(n, 4)}%`;
1595
+ var convertTable = (node, ctx, opts, convertBlocks) => {
1596
+ const rows = node.content || [];
1597
+ if (!rows.length) return null;
1598
+ const { occupied, numCols, placements } = buildGrid(rows);
1599
+ const { percents, totalPt } = columnWidths(placements, numCols);
1600
+ const { vertical, horizontal } = opts.cellPadding;
1601
+ const rowNodes = occupied.map((slots, r) => {
1602
+ const children = [];
1603
+ for (let c = 0; c < numCols; c += 1) {
1604
+ const p = slots == null ? void 0 : slots[c];
1605
+ if (!p) {
1606
+ children.push({ type: "view", style: { width: pct(percents[c]) }, children: [] });
1607
+ continue;
1608
+ }
1609
+ if (p.c !== c) continue;
1610
+ const width = pct(percents.slice(c, c + p.colspan).reduce((a, b) => a + b, 0));
1611
+ const attrs = p.cell.attrs || {};
1612
+ const borders = cellBorders(p, r, c, occupied, opts);
1613
+ const bg = toHexColor(attrs.backgroundColor);
1614
+ const isStart = p.r === r;
1615
+ const isEnd = p.r + p.rowspan - 1 === r;
1616
+ const style = {
1617
+ width,
1618
+ paddingTop: isStart ? vertical : 0,
1619
+ paddingBottom: isEnd ? vertical : 0,
1620
+ paddingLeft: horizontal,
1621
+ paddingRight: horizontal,
1622
+ ...bg ? { backgroundColor: bg } : {},
1623
+ ...borders.borderLeft ? { borderLeft: borders.borderLeft } : {},
1624
+ ...borders.borderRight ? { borderRight: borders.borderRight } : {},
1625
+ ...isStart && borders.borderTop ? { borderTop: borders.borderTop } : {},
1626
+ ...isEnd && borders.borderBottom ? { borderBottom: borders.borderBottom } : {},
1627
+ justifyContent: VALIGN[attrs.verticalAlign] || "flex-start",
1628
+ ...opts.styles[p.cell.type] || {}
1629
+ };
1630
+ const content = isStart ? convertBlocks(p.cell.content || [], {
1631
+ ...ctx,
1632
+ inCell: true,
1633
+ align: attrs.align || null,
1634
+ bold: p.cell.type === "tableHeader"
1635
+ }) : [];
1636
+ children.push({ type: "view", style, children: content });
1637
+ }
1638
+ return {
1639
+ type: "view",
1640
+ style: { flexDirection: "row", ...opts.styles.tableRow || {} },
1641
+ props: opts.keepRowsTogether ? { wrap: false } : void 0,
1642
+ children
1643
+ };
1644
+ });
1645
+ return {
1646
+ type: "view",
1647
+ style: {
1648
+ flexDirection: "column",
1649
+ width: totalPt ? `${totalPt}pt` : "100%",
1650
+ maxWidth: "100%",
1651
+ marginBottom: ctx.inCell ? 2 : opts.paragraphSpacing,
1652
+ ...opts.styles.table || {}
1653
+ },
1654
+ children: rowNodes
1655
+ };
1656
+ };
1657
+
1658
+ // src/pdf/tree.js
1659
+ var PDF_TREE_VERSION = 1;
1660
+ var paragraphStyle = (attrs = {}, ctx, opts, base = {}) => {
1661
+ const style = { ...base };
1662
+ const align = attrs.textAlign || ctx.align;
1663
+ if (align && align !== "left") style.textAlign = align;
1664
+ if (attrs.marginLeft) style.marginLeft = pxToPt(attrs.marginLeft);
1665
+ if (attrs.textIndent) {
1666
+ const indent = pxToPt(attrs.textIndent);
1667
+ style.textIndent = Math.max(indent, -(style.marginLeft || 0));
1668
+ }
1669
+ if (attrs.marginTop) style.marginTop = pxToPt(attrs.marginTop);
1670
+ if (attrs.marginBottom !== null && attrs.marginBottom !== void 0) {
1671
+ style.marginBottom = pxToPt(attrs.marginBottom);
1672
+ }
1673
+ const lineHeight = attrs.lineHeight || opts.lineHeight;
1674
+ if (lineHeight) style.lineHeight = lineHeight;
1675
+ if (ctx.bold) style.fontWeight = "bold";
1676
+ return style;
1677
+ };
1678
+ var textBlock = (node, ctx, opts, base) => {
1679
+ const children = convertInline(node.content, ctx, opts);
1680
+ return {
1681
+ type: "text",
1682
+ style: paragraphStyle(node.attrs, ctx, opts, base),
1683
+ children: children.length ? children : [" "]
1684
+ };
1685
+ };
1686
+ var defaultSpacing = (ctx, opts) => {
1687
+ if (ctx.inCell) return 0;
1688
+ if (ctx.inList) return opts.listItemSpacing;
1689
+ return opts.paragraphSpacing;
1690
+ };
1691
+ var convertList = (node, ctx, opts, convertBlocks) => {
1692
+ const depth = ctx.listDepth || 0;
1693
+ const items = (node.content || []).map((item, index) => {
1694
+ const marker = listMarker(node, index, depth, opts.bullets);
1695
+ return {
1696
+ type: "view",
1697
+ style: { flexDirection: "row", ...opts.styles.listItem || {} },
1698
+ children: [
1699
+ {
1700
+ type: "text",
1701
+ style: { width: opts.listIndent, ...opts.styles.listMarker || {} },
1702
+ children: [marker]
1703
+ },
1704
+ {
1705
+ type: "view",
1706
+ style: { flex: 1 },
1707
+ children: convertBlocks(item.content || [], {
1708
+ ...ctx,
1709
+ inList: true,
1710
+ listDepth: depth + 1
1711
+ })
1712
+ }
1713
+ ]
1714
+ };
1715
+ });
1716
+ return {
1717
+ type: "view",
1718
+ style: {
1719
+ marginBottom: depth ? 0 : defaultSpacing({ ...ctx, inList: false }, opts),
1720
+ ...opts.styles.list || {}
1721
+ },
1722
+ children: items
1723
+ };
1724
+ };
1725
+ var convertBlock = (node, ctx, opts, convertBlocks) => {
1726
+ var _a;
1727
+ switch (node.type) {
1728
+ case "paragraph":
1729
+ return textBlock(node, ctx, opts, {
1730
+ marginBottom: defaultSpacing(ctx, opts),
1731
+ ...opts.styles.paragraph || {}
1732
+ });
1733
+ case "heading": {
1734
+ const level = ((_a = node.attrs) == null ? void 0 : _a.level) || 1;
1735
+ const fontSize = Math.round(opts.fontSize * (opts.headingScale[level] || 1) * 100) / 100;
1736
+ return textBlock(node, { ...ctx, fontSize }, opts, {
1737
+ fontSize,
1738
+ fontWeight: "bold",
1739
+ marginTop: ctx.inCell ? 0 : opts.headingSpacing.top,
1740
+ marginBottom: ctx.inCell ? 0 : opts.headingSpacing.bottom,
1741
+ ...opts.styles.heading || {},
1742
+ ...opts.styles[`heading${level}`] || {}
1743
+ });
1744
+ }
1745
+ case "bulletList":
1746
+ case "orderedList":
1747
+ return convertList(node, ctx, opts, convertBlocks);
1748
+ case "table":
1749
+ return convertTable(node, ctx, opts, convertBlocks);
1750
+ case "blockquote":
1751
+ return {
1752
+ type: "view",
1753
+ style: {
1754
+ borderLeft: "2pt solid #cccccc",
1755
+ paddingLeft: 8,
1756
+ marginBottom: defaultSpacing(ctx, opts),
1757
+ ...opts.styles.blockquote || {}
1758
+ },
1759
+ children: convertBlocks(node.content || [], ctx)
1760
+ };
1761
+ case "horizontalRule":
1762
+ return {
1763
+ type: "view",
1764
+ style: {
1765
+ borderBottom: "0.75pt solid #999999",
1766
+ marginVertical: 4,
1767
+ ...opts.styles.horizontalRule || {}
1768
+ },
1769
+ children: []
1770
+ };
1771
+ case "image": {
1772
+ const { src, width, height } = node.attrs || {};
1773
+ if (!src) return null;
1774
+ const style = { maxWidth: "100%", marginBottom: defaultSpacing(ctx, opts) };
1775
+ if (width) style.width = pxToPt(width);
1776
+ if (height) style.height = pxToPt(height);
1777
+ return { type: "image", src, style: { ...style, ...opts.styles.image || {} } };
1778
+ }
1779
+ default:
1780
+ return node.content ? { type: "view", style: {}, children: convertBlocks(node.content, ctx) } : null;
1781
+ }
1782
+ };
1783
+ var jsonToPdfTree = (json, options) => {
1784
+ const opts = resolveOptions(options);
1785
+ const ctx = { fontSize: opts.fontSize };
1786
+ const convertBlocks = (nodes, c) => (nodes || []).map((n) => convertBlock(n, c, opts, convertBlocks)).filter(Boolean);
1787
+ const rootStyle = opts.explicitFontSize ? { fontSize: opts.fontSize } : {};
1788
+ if (opts.fontFamily) rootStyle.fontFamily = opts.fontFamily;
1789
+ if (opts.color) rootStyle.color = opts.color;
1790
+ return {
1791
+ type: "root",
1792
+ version: PDF_TREE_VERSION,
1793
+ style: { ...rootStyle, ...opts.styles.root || {} },
1794
+ children: convertBlocks((json == null ? void 0 : json.content) || [], ctx)
1795
+ };
1796
+ };
1797
+ var isPdfTree = (value) => !!value && typeof value === "object" && value.type === "root" && Array.isArray(value.children);
1798
+
1799
+ // src/output/convert.js
1800
+ import { getSchema } from "@tiptap/core";
1801
+ import { DOMParser as PMDOMParser, DOMSerializer, Node as PMNode } from "@tiptap/pm/model";
1802
+ var cachedSchema = null;
1803
+ var getEditorSchema = () => {
1804
+ if (!cachedSchema) cachedSchema = getSchema(buildSchemaExtensions({ nodeIds: true }));
1805
+ return cachedSchema;
1806
+ };
1807
+ var EMPTY_DOC = { type: "doc", content: [{ type: "paragraph" }] };
1808
+ var isDocJson = (value) => !!value && typeof value === "object" && value.type === "doc";
1809
+ var looksLikeHtml = (str) => /<\/?[a-z][\s\S]*>/i.test(str);
1810
+ var escapeHtml = (str) => str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1811
+ var textToHtml = (text) => String(text).split(/\r?\n/).map((line) => `<p>${escapeHtml(line)}</p>`).join("");
1812
+ var normalizeHtmlInput = (value) => {
1813
+ if (value === null || value === void 0) return "";
1814
+ const str = String(value);
1815
+ if (!str.trim()) return "";
1816
+ return looksLikeHtml(str) ? cleanHtml(str) : textToHtml(str);
1817
+ };
1818
+ var htmlToJSON = (html) => {
1819
+ const schema = getEditorSchema();
1820
+ const clean = normalizeHtmlInput(html);
1821
+ if (!clean) return EMPTY_DOC;
1822
+ if (typeof window === "undefined" || typeof document === "undefined") {
1823
+ throw new Error("htmlToJSON needs a DOM (browser, jsdom or happy-dom)");
1824
+ }
1825
+ const container = document.createElement("div");
1826
+ container.innerHTML = clean;
1827
+ return PMDOMParser.fromSchema(schema).parse(container).toJSON();
1828
+ };
1829
+ var toDocJson = (value) => {
1830
+ if (isDocJson(value)) return value;
1831
+ return htmlToJSON(value);
1832
+ };
1833
+ var jsonToHTML = (json) => {
1834
+ const schema = getEditorSchema();
1835
+ const node = PMNode.fromJSON(schema, isDocJson(json) ? json : EMPTY_DOC);
1836
+ const fragment = DOMSerializer.fromSchema(schema).serializeFragment(node.content);
1837
+ const container = document.createElement("div");
1838
+ container.appendChild(fragment);
1839
+ return container.innerHTML;
1840
+ };
1841
+ var htmlToText = (html) => jsonToText(htmlToJSON(html));
1842
+
1843
+ export {
1844
+ BLOCK_TYPES,
1845
+ DEFAULT_FONT_FAMILIES,
1846
+ DEFAULT_FONT_SIZES,
1847
+ LINE_HEIGHTS,
1848
+ COLOR_SWATCHES,
1849
+ DEFAULT_TOOLBAR,
1850
+ TABLE_PICKER_SIZE,
1851
+ PARAGRAPH_SPACING,
1852
+ TEXT_CASES,
1853
+ TABLE_BORDER_OPTIONS,
1854
+ INDENT_STEP_PX,
1855
+ matchSpacingPreset,
1856
+ ParagraphFormat,
1857
+ RichTableCell,
1858
+ RichTableHeader,
1859
+ SILENT_META,
1860
+ NODE_ID_ATTRIBUTE,
1861
+ NODE_ID_TYPES,
1862
+ ensureNodeIds,
1863
+ NodeIds,
1864
+ readAsDataUrl,
1865
+ insertImages,
1866
+ getTableBorderState,
1867
+ buildSchemaExtensions,
1868
+ buildEditorExtensions,
1869
+ isWordHtml,
1870
+ cleanHtml,
1871
+ jsonToText,
1872
+ STANDARD_FONTS,
1873
+ DEFAULT_PDF_OPTIONS,
1874
+ PDF_TREE_VERSION,
1875
+ jsonToPdfTree,
1876
+ isPdfTree,
1877
+ getEditorSchema,
1878
+ EMPTY_DOC,
1879
+ isDocJson,
1880
+ textToHtml,
1881
+ normalizeHtmlInput,
1882
+ htmlToJSON,
1883
+ toDocJson,
1884
+ jsonToHTML,
1885
+ htmlToText
1886
+ };
1887
+ //# sourceMappingURL=chunk-JQEG2JQ7.js.map