@kedataindo/docflow-core 0.0.49 → 0.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +177 -40
- package/dist/index.d.cts +23 -8
- package/dist/index.d.ts +23 -8
- package/dist/index.js +175 -40
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -49,11 +49,13 @@ __export(index_exports, {
|
|
|
49
49
|
createEditor: () => createEditor,
|
|
50
50
|
createPerformanceMonitor: () => createPerformanceMonitor,
|
|
51
51
|
definePlugin: () => definePlugin,
|
|
52
|
+
detectClipboardSource: () => detectClipboardSource,
|
|
52
53
|
findMatches: () => findMatches,
|
|
53
54
|
getSearchState: () => getSearchState,
|
|
54
55
|
httpKeyStorage: () => httpKeyStorage,
|
|
55
56
|
localStorageKeyStorage: () => localStorageKeyStorage,
|
|
56
57
|
memoryKeyStorage: () => memoryKeyStorage,
|
|
58
|
+
normalizeClipboardHTML: () => normalizeClipboardHTML,
|
|
57
59
|
openaiCompatibleProvider: () => openaiCompatibleProvider,
|
|
58
60
|
replaceAll: () => replaceAll,
|
|
59
61
|
replaceCurrent: () => replaceCurrent,
|
|
@@ -460,13 +462,13 @@ function replaceAll(editor, replacement) {
|
|
|
460
462
|
return s.matches.length;
|
|
461
463
|
}
|
|
462
464
|
|
|
463
|
-
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.
|
|
465
|
+
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.0_patch_hash=4857789ba5672a185fb96bdd854e03e0c6ec8ed26abfeea_16730b2b361cd3f71113c52459ed62fd/node_modules/tiptap-pagination-plus/dist/PaginationPlus.js
|
|
464
466
|
var import_core4 = require("@tiptap/core");
|
|
465
467
|
var import_state3 = require("@tiptap/pm/state");
|
|
466
468
|
var import_transform = require("@tiptap/pm/transform");
|
|
467
469
|
var import_view3 = require("@tiptap/pm/view");
|
|
468
470
|
|
|
469
|
-
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.
|
|
471
|
+
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.0_patch_hash=4857789ba5672a185fb96bdd854e03e0c6ec8ed26abfeea_16730b2b361cd3f71113c52459ed62fd/node_modules/tiptap-pagination-plus/dist/utils.js
|
|
470
472
|
var updateCssVariables = (targetNode, config) => {
|
|
471
473
|
const cssVariables = {
|
|
472
474
|
"rm-page-height": `${config.pageHeight}px`,
|
|
@@ -640,7 +642,7 @@ var footerClickEvent = (pageNumber, onFooterClick) => {
|
|
|
640
642
|
};
|
|
641
643
|
};
|
|
642
644
|
|
|
643
|
-
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.
|
|
645
|
+
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.0_patch_hash=4857789ba5672a185fb96bdd854e03e0c6ec8ed26abfeea_16730b2b361cd3f71113c52459ed62fd/node_modules/tiptap-pagination-plus/dist/PaginationPlus.js
|
|
644
646
|
var page_count_meta_key = "PAGE_COUNT_META_KEY";
|
|
645
647
|
var key = new import_state3.PluginKey("brDecoration");
|
|
646
648
|
function buildDecorations(doc) {
|
|
@@ -807,11 +809,6 @@ var PaginationPlus = import_core4.Extension.create({
|
|
|
807
809
|
width: 100%;
|
|
808
810
|
display: contents;
|
|
809
811
|
}
|
|
810
|
-
.rm-with-pagination table tbody{
|
|
811
|
-
display: table;
|
|
812
|
-
max-height: 300px;
|
|
813
|
-
overflow-y: auto;
|
|
814
|
-
}
|
|
815
812
|
.rm-with-pagination table tbody > tr{
|
|
816
813
|
display: table-row !important;
|
|
817
814
|
}
|
|
@@ -1134,6 +1131,7 @@ var getExistingPageCount = (view) => {
|
|
|
1134
1131
|
}
|
|
1135
1132
|
return 0;
|
|
1136
1133
|
};
|
|
1134
|
+
var pageGapTracker = /* @__PURE__ */ new WeakMap();
|
|
1137
1135
|
var calculatePageCount = (view, pageOptions, headerHeight = 0, footerHeight = 0) => {
|
|
1138
1136
|
var _a;
|
|
1139
1137
|
const editorDom = view.dom;
|
|
@@ -1151,8 +1149,36 @@ var calculatePageCount = (view, pageOptions, headerHeight = 0, footerHeight = 0)
|
|
|
1151
1149
|
const lastPageGap = lastElementRect.bottom - lastPageBreakRect.bottom;
|
|
1152
1150
|
if (lastPageGap > 0) {
|
|
1153
1151
|
const addPage = Math.ceil(lastPageGap / pageContentAreaHeight);
|
|
1152
|
+
if (lastElementRect.height > pageContentAreaHeight) {
|
|
1153
|
+
const isSplittableTable = (lastElementOfEditor === null || lastElementOfEditor === void 0 ? void 0 : lastElementOfEditor.tagName) === "TABLE" && lastElementOfEditor.hasAttribute("data-tps-splittable");
|
|
1154
|
+
if (!isSplittableTable) {
|
|
1155
|
+
return currentPageCount;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
const prev = pageGapTracker.get(editorDom);
|
|
1159
|
+
if (prev && prev.pages < currentPageCount && lastPageGap > prev.gap - 2) {
|
|
1160
|
+
return currentPageCount;
|
|
1161
|
+
}
|
|
1162
|
+
pageGapTracker.set(editorDom, { gap: lastPageGap, pages: currentPageCount });
|
|
1163
|
+
let totalEditorContentHeight = 0;
|
|
1164
|
+
for (const child of editorDom.children) {
|
|
1165
|
+
if (!(child instanceof HTMLElement)) continue;
|
|
1166
|
+
if (child.hasAttribute("data-rm-pagination")) continue;
|
|
1167
|
+
if (child.classList.contains("rm-page-break")) continue;
|
|
1168
|
+
totalEditorContentHeight += child.getBoundingClientRect().height;
|
|
1169
|
+
}
|
|
1170
|
+
const maxPagesByContent = Math.ceil(totalEditorContentHeight / pageContentAreaHeight);
|
|
1171
|
+
const contentCap = Math.max(currentPageCount, maxPagesByContent + 1);
|
|
1172
|
+
if (currentPageCount + addPage > contentCap) {
|
|
1173
|
+
return contentCap;
|
|
1174
|
+
}
|
|
1175
|
+
const MAX_PAGES = 1e3;
|
|
1176
|
+
if (currentPageCount + addPage > MAX_PAGES) {
|
|
1177
|
+
return currentPageCount;
|
|
1178
|
+
}
|
|
1154
1179
|
return currentPageCount + addPage;
|
|
1155
1180
|
} else {
|
|
1181
|
+
pageGapTracker.delete(editorDom);
|
|
1156
1182
|
const allBreaksAfterLastElement = Array.from(paginationElement.querySelectorAll(".breaker"));
|
|
1157
1183
|
const allBreaksAfterLastElementRect = allBreaksAfterLastElement.filter((element) => element.getBoundingClientRect().top > lastElementRect.bottom);
|
|
1158
1184
|
const removePage = allBreaksAfterLastElementRect.length;
|
|
@@ -1279,7 +1305,7 @@ function createDecoration(pageOptions, headerHeightMap, footerHeightMap) {
|
|
|
1279
1305
|
return [pageWidget, firstHeaderWidget];
|
|
1280
1306
|
}
|
|
1281
1307
|
|
|
1282
|
-
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.
|
|
1308
|
+
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.0_patch_hash=4857789ba5672a185fb96bdd854e03e0c6ec8ed26abfeea_16730b2b361cd3f71113c52459ed62fd/node_modules/tiptap-pagination-plus/dist/constants.js
|
|
1283
1309
|
var A4_PAGE_SIZE = getPageSize(1123, 794, 95, 95, 76, 76);
|
|
1284
1310
|
var A3_PAGE_SIZE = getPageSize(1591, 1123, 95, 95, 76, 76);
|
|
1285
1311
|
var A5_PAGE_SIZE = getPageSize(794, 419, 76, 76, 57, 57);
|
|
@@ -1477,41 +1503,150 @@ function createPerformanceMonitor(options) {
|
|
|
1477
1503
|
return monitor;
|
|
1478
1504
|
}
|
|
1479
1505
|
|
|
1480
|
-
// src/
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1506
|
+
// src/pasteNormalization.ts
|
|
1507
|
+
var KEEP_STYLE_PROPS = /* @__PURE__ */ new Set(["color", "background-color"]);
|
|
1508
|
+
var REMOVE_TAGS = /* @__PURE__ */ new Set([
|
|
1509
|
+
"meta",
|
|
1510
|
+
"style",
|
|
1511
|
+
"link",
|
|
1512
|
+
"base",
|
|
1513
|
+
"title",
|
|
1514
|
+
"script",
|
|
1515
|
+
"iframe",
|
|
1516
|
+
"object",
|
|
1517
|
+
"embed",
|
|
1518
|
+
"form",
|
|
1519
|
+
"input",
|
|
1520
|
+
"button",
|
|
1521
|
+
"select",
|
|
1522
|
+
"textarea",
|
|
1523
|
+
"noscript",
|
|
1524
|
+
// Word Office namespace dump paragraph
|
|
1525
|
+
"o:p"
|
|
1526
|
+
]);
|
|
1527
|
+
var KEEP_ATTRS = {
|
|
1528
|
+
a: /* @__PURE__ */ new Set(["href", "style"]),
|
|
1529
|
+
img: /* @__PURE__ */ new Set(["src", "alt", "style"]),
|
|
1530
|
+
td: /* @__PURE__ */ new Set(["colspan", "rowspan", "style"]),
|
|
1531
|
+
th: /* @__PURE__ */ new Set(["colspan", "rowspan", "style"]),
|
|
1532
|
+
col: /* @__PURE__ */ new Set(["span"]),
|
|
1533
|
+
"*": /* @__PURE__ */ new Set(["style"])
|
|
1534
|
+
};
|
|
1535
|
+
function detectClipboardSource(html) {
|
|
1536
|
+
if (/id\s*=\s*["']docs-internal/i.test(html)) return "google-docs";
|
|
1537
|
+
if (/<o:p[\s>]|xmlns(:w|:o)?=|class\s*=\s*["']?Mso|mso-/i.test(html)) return "word";
|
|
1538
|
+
return "generic";
|
|
1539
|
+
}
|
|
1540
|
+
function parseStyleDeclarations(style) {
|
|
1541
|
+
const map = /* @__PURE__ */ new Map();
|
|
1542
|
+
for (const decl of style.split(";")) {
|
|
1543
|
+
const idx = decl.indexOf(":");
|
|
1544
|
+
if (idx < 0) continue;
|
|
1545
|
+
const prop = decl.slice(0, idx).trim().toLowerCase();
|
|
1546
|
+
const val = decl.slice(idx + 1).trim();
|
|
1547
|
+
if (prop && val) map.set(prop, val);
|
|
1548
|
+
}
|
|
1549
|
+
return map;
|
|
1550
|
+
}
|
|
1551
|
+
function cleanStyleAttr(el) {
|
|
1552
|
+
const style = el.getAttribute("style");
|
|
1553
|
+
if (!style) return;
|
|
1554
|
+
const kept = [];
|
|
1555
|
+
parseStyleDeclarations(style).forEach((val, prop) => {
|
|
1556
|
+
if (KEEP_STYLE_PROPS.has(prop)) kept.push(`${prop}: ${val}`);
|
|
1557
|
+
});
|
|
1558
|
+
if (kept.length) el.setAttribute("style", kept.join("; "));
|
|
1559
|
+
else el.removeAttribute("style");
|
|
1560
|
+
}
|
|
1561
|
+
function cleanAttrs(el) {
|
|
1562
|
+
const keep = /* @__PURE__ */ new Set([...KEEP_ATTRS["*"] ?? [], ...KEEP_ATTRS[el.tagName.toLowerCase()] ?? []]);
|
|
1563
|
+
for (const attr of Array.from(el.attributes)) {
|
|
1564
|
+
if (!keep.has(attr.name)) el.removeAttribute(attr.name);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
function unwrap(el) {
|
|
1568
|
+
const parent = el.parentNode;
|
|
1569
|
+
if (!parent) return;
|
|
1570
|
+
while (el.firstChild) parent.insertBefore(el.firstChild, el);
|
|
1571
|
+
parent.removeChild(el);
|
|
1572
|
+
}
|
|
1573
|
+
function convertHighlightSpan(span, styles) {
|
|
1574
|
+
const bg = styles.get("background-color") ?? styles.get("background");
|
|
1575
|
+
if (!bg) return;
|
|
1576
|
+
const color = styles.get("color");
|
|
1577
|
+
const mark = span.ownerDocument.createElement("mark");
|
|
1578
|
+
mark.setAttribute("style", `background-color: ${bg}`);
|
|
1579
|
+
if (color) {
|
|
1580
|
+
const inner = span.ownerDocument.createElement("span");
|
|
1581
|
+
inner.setAttribute("style", `color: ${color}`);
|
|
1582
|
+
while (span.firstChild) inner.appendChild(span.firstChild);
|
|
1583
|
+
mark.appendChild(inner);
|
|
1584
|
+
} else {
|
|
1585
|
+
while (span.firstChild) mark.appendChild(span.firstChild);
|
|
1586
|
+
}
|
|
1587
|
+
span.replaceWith(mark);
|
|
1588
|
+
}
|
|
1589
|
+
function normalizeElement(el, source) {
|
|
1590
|
+
if (el.tagName === "SPAN") {
|
|
1591
|
+
const styles = parseStyleDeclarations(el.getAttribute("style") ?? "");
|
|
1592
|
+
if (styles.has("background-color") || styles.has("background")) {
|
|
1593
|
+
convertHighlightSpan(el, styles);
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
cleanStyleAttr(el);
|
|
1597
|
+
cleanAttrs(el);
|
|
1598
|
+
if (el.attributes.length === 0) unwrap(el);
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
if (el.tagName === "B") {
|
|
1602
|
+
const styles = parseStyleDeclarations(el.getAttribute("style") ?? "");
|
|
1603
|
+
if (styles.get("font-weight")?.replace(/\s/g, "") === "normal") {
|
|
1604
|
+
unwrap(el);
|
|
1605
|
+
return;
|
|
1505
1606
|
}
|
|
1506
1607
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1608
|
+
cleanStyleAttr(el);
|
|
1609
|
+
cleanAttrs(el);
|
|
1610
|
+
if (source === "word") {
|
|
1611
|
+
el.removeAttribute("class");
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
function removeComments(root) {
|
|
1615
|
+
const walker = root.ownerDocument.createTreeWalker(
|
|
1616
|
+
root,
|
|
1617
|
+
128
|
|
1618
|
+
/* NodeFilter.SHOW_COMMENT */
|
|
1510
1619
|
);
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1620
|
+
const comments = [];
|
|
1621
|
+
let node = walker.nextNode();
|
|
1622
|
+
while (node) {
|
|
1623
|
+
comments.push(node);
|
|
1624
|
+
node = walker.nextNode();
|
|
1625
|
+
}
|
|
1626
|
+
for (const c of comments) c.parentNode?.removeChild(c);
|
|
1514
1627
|
}
|
|
1628
|
+
function normalizeClipboardHTML(html) {
|
|
1629
|
+
if (!html || typeof html !== "string") return html ?? "";
|
|
1630
|
+
if (!/[<][a-zA-Z]/.test(html)) return html;
|
|
1631
|
+
const source = detectClipboardSource(html);
|
|
1632
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
1633
|
+
removeComments(doc.body);
|
|
1634
|
+
for (const tag of REMOVE_TAGS) {
|
|
1635
|
+
for (const el of Array.from(doc.body.getElementsByTagName(tag))) {
|
|
1636
|
+
el.parentNode?.removeChild(el);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
const walk = (node) => {
|
|
1640
|
+
if (node.nodeType !== 1) return;
|
|
1641
|
+
for (const child of Array.from(node.childNodes)) walk(child);
|
|
1642
|
+
normalizeElement(node, source);
|
|
1643
|
+
};
|
|
1644
|
+
for (const child of Array.from(doc.body.childNodes)) walk(child);
|
|
1645
|
+
return doc.body.innerHTML;
|
|
1646
|
+
}
|
|
1647
|
+
var sanitizePastedHTML = normalizeClipboardHTML;
|
|
1648
|
+
|
|
1649
|
+
// src/Editor.ts
|
|
1515
1650
|
function migrateContent(content) {
|
|
1516
1651
|
if (!content) return content;
|
|
1517
1652
|
if (typeof content === "string") {
|
|
@@ -2473,11 +2608,13 @@ function openaiCompatibleProvider(config) {
|
|
|
2473
2608
|
createEditor,
|
|
2474
2609
|
createPerformanceMonitor,
|
|
2475
2610
|
definePlugin,
|
|
2611
|
+
detectClipboardSource,
|
|
2476
2612
|
findMatches,
|
|
2477
2613
|
getSearchState,
|
|
2478
2614
|
httpKeyStorage,
|
|
2479
2615
|
localStorageKeyStorage,
|
|
2480
2616
|
memoryKeyStorage,
|
|
2617
|
+
normalizeClipboardHTML,
|
|
2481
2618
|
openaiCompatibleProvider,
|
|
2482
2619
|
replaceAll,
|
|
2483
2620
|
replaceCurrent,
|
package/dist/index.d.cts
CHANGED
|
@@ -186,15 +186,30 @@ declare class PerformanceMonitor {
|
|
|
186
186
|
declare function createPerformanceMonitor(options?: PerformanceMonitorOptions): PerformanceMonitor;
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* and HTML comments that are common in rich clipboard data but not valid
|
|
192
|
-
* in the ProseMirror schema.
|
|
189
|
+
* DOM-based clipboard HTML normalization (Stage 2 of the paste pipeline —
|
|
190
|
+
* docs/plans/clipboard-paste-pipeline-plan.md).
|
|
193
191
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
192
|
+
* Replaces the old regex-based sanitizePastedHTML: pasted HTML from Google
|
|
193
|
+
* Docs, Word, Notion, or generic web pages is parsed into an inert Document,
|
|
194
|
+
* normalized against a whitelist, and serialized for ProseMirror's parser.
|
|
195
|
+
*
|
|
196
|
+
* Design decisions (see plan §3):
|
|
197
|
+
* - Typography normalizes to destination styles: font-family, font-size,
|
|
198
|
+
* line-height, margins and padding are stripped; only `color` and
|
|
199
|
+
* `background-color` survive as inline styles.
|
|
200
|
+
* - One pipeline for all HTML sources; detectClipboardSource only tunes
|
|
201
|
+
* which wrapper-stripping rules run.
|
|
202
|
+
* - Idempotent by construction: normalizing twice changes nothing.
|
|
203
|
+
*/
|
|
204
|
+
type ClipboardSource = 'google-docs' | 'word' | 'generic';
|
|
205
|
+
declare function detectClipboardSource(html: string): ClipboardSource;
|
|
206
|
+
declare function normalizeClipboardHTML(html: string): string;
|
|
207
|
+
/**
|
|
208
|
+
* Backward-compatible name kept for existing callers
|
|
209
|
+
* (transformPastedHTML in Editor.ts, handlePaste in DocsEditor.vue).
|
|
196
210
|
*/
|
|
197
|
-
declare
|
|
211
|
+
declare const sanitizePastedHTML: typeof normalizeClipboardHTML;
|
|
212
|
+
|
|
198
213
|
interface EditorOptions {
|
|
199
214
|
target?: HTMLElement;
|
|
200
215
|
content?: object | string;
|
|
@@ -380,4 +395,4 @@ declare class SubdocumentProvider {
|
|
|
380
395
|
destroy(): void;
|
|
381
396
|
}
|
|
382
397
|
|
|
383
|
-
export { AIDraftFn, AIStreamFn, type AwarenessState, BlockAttributesExtension, CitationPort, type CollaborationOptions, type CollaborationSetup, type DocsEditor, type DocsEditorPlugin, EditorContextExtension, type EditorContextOptions, type EditorOptions, FontSizeExtension, ImageUploadHandler, type MonitorMetric, PerformanceMonitor, type PerformanceMonitorOptions, SearchAndReplaceExtension, type SearchMatch, type SearchState, type SlashCommand, type SubdocState, SubdocumentProvider, type SubdocumentProviderOptions, type ToolbarItem, clearSearch, collaborationExtensions, collectExtensions, createActionMap, createCollaboration, createEditor, createPerformanceMonitor, definePlugin, findMatches, getSearchState, replaceAll, replaceCurrent, resolveAction, sanitizePastedHTML, searchAndReplaceKey, searchNext, searchPrev, setSearchQuery };
|
|
398
|
+
export { AIDraftFn, AIStreamFn, type AwarenessState, BlockAttributesExtension, CitationPort, type ClipboardSource, type CollaborationOptions, type CollaborationSetup, type DocsEditor, type DocsEditorPlugin, EditorContextExtension, type EditorContextOptions, type EditorOptions, FontSizeExtension, ImageUploadHandler, type MonitorMetric, PerformanceMonitor, type PerformanceMonitorOptions, SearchAndReplaceExtension, type SearchMatch, type SearchState, type SlashCommand, type SubdocState, SubdocumentProvider, type SubdocumentProviderOptions, type ToolbarItem, clearSearch, collaborationExtensions, collectExtensions, createActionMap, createCollaboration, createEditor, createPerformanceMonitor, definePlugin, detectClipboardSource, findMatches, getSearchState, normalizeClipboardHTML, replaceAll, replaceCurrent, resolveAction, sanitizePastedHTML, searchAndReplaceKey, searchNext, searchPrev, setSearchQuery };
|
package/dist/index.d.ts
CHANGED
|
@@ -186,15 +186,30 @@ declare class PerformanceMonitor {
|
|
|
186
186
|
declare function createPerformanceMonitor(options?: PerformanceMonitorOptions): PerformanceMonitor;
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* and HTML comments that are common in rich clipboard data but not valid
|
|
192
|
-
* in the ProseMirror schema.
|
|
189
|
+
* DOM-based clipboard HTML normalization (Stage 2 of the paste pipeline —
|
|
190
|
+
* docs/plans/clipboard-paste-pipeline-plan.md).
|
|
193
191
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
192
|
+
* Replaces the old regex-based sanitizePastedHTML: pasted HTML from Google
|
|
193
|
+
* Docs, Word, Notion, or generic web pages is parsed into an inert Document,
|
|
194
|
+
* normalized against a whitelist, and serialized for ProseMirror's parser.
|
|
195
|
+
*
|
|
196
|
+
* Design decisions (see plan §3):
|
|
197
|
+
* - Typography normalizes to destination styles: font-family, font-size,
|
|
198
|
+
* line-height, margins and padding are stripped; only `color` and
|
|
199
|
+
* `background-color` survive as inline styles.
|
|
200
|
+
* - One pipeline for all HTML sources; detectClipboardSource only tunes
|
|
201
|
+
* which wrapper-stripping rules run.
|
|
202
|
+
* - Idempotent by construction: normalizing twice changes nothing.
|
|
203
|
+
*/
|
|
204
|
+
type ClipboardSource = 'google-docs' | 'word' | 'generic';
|
|
205
|
+
declare function detectClipboardSource(html: string): ClipboardSource;
|
|
206
|
+
declare function normalizeClipboardHTML(html: string): string;
|
|
207
|
+
/**
|
|
208
|
+
* Backward-compatible name kept for existing callers
|
|
209
|
+
* (transformPastedHTML in Editor.ts, handlePaste in DocsEditor.vue).
|
|
196
210
|
*/
|
|
197
|
-
declare
|
|
211
|
+
declare const sanitizePastedHTML: typeof normalizeClipboardHTML;
|
|
212
|
+
|
|
198
213
|
interface EditorOptions {
|
|
199
214
|
target?: HTMLElement;
|
|
200
215
|
content?: object | string;
|
|
@@ -380,4 +395,4 @@ declare class SubdocumentProvider {
|
|
|
380
395
|
destroy(): void;
|
|
381
396
|
}
|
|
382
397
|
|
|
383
|
-
export { AIDraftFn, AIStreamFn, type AwarenessState, BlockAttributesExtension, CitationPort, type CollaborationOptions, type CollaborationSetup, type DocsEditor, type DocsEditorPlugin, EditorContextExtension, type EditorContextOptions, type EditorOptions, FontSizeExtension, ImageUploadHandler, type MonitorMetric, PerformanceMonitor, type PerformanceMonitorOptions, SearchAndReplaceExtension, type SearchMatch, type SearchState, type SlashCommand, type SubdocState, SubdocumentProvider, type SubdocumentProviderOptions, type ToolbarItem, clearSearch, collaborationExtensions, collectExtensions, createActionMap, createCollaboration, createEditor, createPerformanceMonitor, definePlugin, findMatches, getSearchState, replaceAll, replaceCurrent, resolveAction, sanitizePastedHTML, searchAndReplaceKey, searchNext, searchPrev, setSearchQuery };
|
|
398
|
+
export { AIDraftFn, AIStreamFn, type AwarenessState, BlockAttributesExtension, CitationPort, type ClipboardSource, type CollaborationOptions, type CollaborationSetup, type DocsEditor, type DocsEditorPlugin, EditorContextExtension, type EditorContextOptions, type EditorOptions, FontSizeExtension, ImageUploadHandler, type MonitorMetric, PerformanceMonitor, type PerformanceMonitorOptions, SearchAndReplaceExtension, type SearchMatch, type SearchState, type SlashCommand, type SubdocState, SubdocumentProvider, type SubdocumentProviderOptions, type ToolbarItem, clearSearch, collaborationExtensions, collectExtensions, createActionMap, createCollaboration, createEditor, createPerformanceMonitor, definePlugin, detectClipboardSource, findMatches, getSearchState, normalizeClipboardHTML, replaceAll, replaceCurrent, resolveAction, sanitizePastedHTML, searchAndReplaceKey, searchNext, searchPrev, setSearchQuery };
|
package/dist/index.js
CHANGED
|
@@ -402,13 +402,13 @@ function replaceAll(editor, replacement) {
|
|
|
402
402
|
return s.matches.length;
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
-
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.
|
|
405
|
+
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.0_patch_hash=4857789ba5672a185fb96bdd854e03e0c6ec8ed26abfeea_16730b2b361cd3f71113c52459ed62fd/node_modules/tiptap-pagination-plus/dist/PaginationPlus.js
|
|
406
406
|
import { Extension as Extension4 } from "@tiptap/core";
|
|
407
407
|
import { Plugin as Plugin3, PluginKey as PluginKey3 } from "@tiptap/pm/state";
|
|
408
408
|
import { ReplaceStep, ReplaceAroundStep, AddMarkStep, RemoveMarkStep, RemoveNodeMarkStep, AttrStep } from "@tiptap/pm/transform";
|
|
409
409
|
import { Decoration as Decoration3, DecorationSet as DecorationSet3 } from "@tiptap/pm/view";
|
|
410
410
|
|
|
411
|
-
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.
|
|
411
|
+
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.0_patch_hash=4857789ba5672a185fb96bdd854e03e0c6ec8ed26abfeea_16730b2b361cd3f71113c52459ed62fd/node_modules/tiptap-pagination-plus/dist/utils.js
|
|
412
412
|
var updateCssVariables = (targetNode, config) => {
|
|
413
413
|
const cssVariables = {
|
|
414
414
|
"rm-page-height": `${config.pageHeight}px`,
|
|
@@ -582,7 +582,7 @@ var footerClickEvent = (pageNumber, onFooterClick) => {
|
|
|
582
582
|
};
|
|
583
583
|
};
|
|
584
584
|
|
|
585
|
-
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.
|
|
585
|
+
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.0_patch_hash=4857789ba5672a185fb96bdd854e03e0c6ec8ed26abfeea_16730b2b361cd3f71113c52459ed62fd/node_modules/tiptap-pagination-plus/dist/PaginationPlus.js
|
|
586
586
|
var page_count_meta_key = "PAGE_COUNT_META_KEY";
|
|
587
587
|
var key = new PluginKey3("brDecoration");
|
|
588
588
|
function buildDecorations(doc) {
|
|
@@ -749,11 +749,6 @@ var PaginationPlus = Extension4.create({
|
|
|
749
749
|
width: 100%;
|
|
750
750
|
display: contents;
|
|
751
751
|
}
|
|
752
|
-
.rm-with-pagination table tbody{
|
|
753
|
-
display: table;
|
|
754
|
-
max-height: 300px;
|
|
755
|
-
overflow-y: auto;
|
|
756
|
-
}
|
|
757
752
|
.rm-with-pagination table tbody > tr{
|
|
758
753
|
display: table-row !important;
|
|
759
754
|
}
|
|
@@ -1076,6 +1071,7 @@ var getExistingPageCount = (view) => {
|
|
|
1076
1071
|
}
|
|
1077
1072
|
return 0;
|
|
1078
1073
|
};
|
|
1074
|
+
var pageGapTracker = /* @__PURE__ */ new WeakMap();
|
|
1079
1075
|
var calculatePageCount = (view, pageOptions, headerHeight = 0, footerHeight = 0) => {
|
|
1080
1076
|
var _a;
|
|
1081
1077
|
const editorDom = view.dom;
|
|
@@ -1093,8 +1089,36 @@ var calculatePageCount = (view, pageOptions, headerHeight = 0, footerHeight = 0)
|
|
|
1093
1089
|
const lastPageGap = lastElementRect.bottom - lastPageBreakRect.bottom;
|
|
1094
1090
|
if (lastPageGap > 0) {
|
|
1095
1091
|
const addPage = Math.ceil(lastPageGap / pageContentAreaHeight);
|
|
1092
|
+
if (lastElementRect.height > pageContentAreaHeight) {
|
|
1093
|
+
const isSplittableTable = (lastElementOfEditor === null || lastElementOfEditor === void 0 ? void 0 : lastElementOfEditor.tagName) === "TABLE" && lastElementOfEditor.hasAttribute("data-tps-splittable");
|
|
1094
|
+
if (!isSplittableTable) {
|
|
1095
|
+
return currentPageCount;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
const prev = pageGapTracker.get(editorDom);
|
|
1099
|
+
if (prev && prev.pages < currentPageCount && lastPageGap > prev.gap - 2) {
|
|
1100
|
+
return currentPageCount;
|
|
1101
|
+
}
|
|
1102
|
+
pageGapTracker.set(editorDom, { gap: lastPageGap, pages: currentPageCount });
|
|
1103
|
+
let totalEditorContentHeight = 0;
|
|
1104
|
+
for (const child of editorDom.children) {
|
|
1105
|
+
if (!(child instanceof HTMLElement)) continue;
|
|
1106
|
+
if (child.hasAttribute("data-rm-pagination")) continue;
|
|
1107
|
+
if (child.classList.contains("rm-page-break")) continue;
|
|
1108
|
+
totalEditorContentHeight += child.getBoundingClientRect().height;
|
|
1109
|
+
}
|
|
1110
|
+
const maxPagesByContent = Math.ceil(totalEditorContentHeight / pageContentAreaHeight);
|
|
1111
|
+
const contentCap = Math.max(currentPageCount, maxPagesByContent + 1);
|
|
1112
|
+
if (currentPageCount + addPage > contentCap) {
|
|
1113
|
+
return contentCap;
|
|
1114
|
+
}
|
|
1115
|
+
const MAX_PAGES = 1e3;
|
|
1116
|
+
if (currentPageCount + addPage > MAX_PAGES) {
|
|
1117
|
+
return currentPageCount;
|
|
1118
|
+
}
|
|
1096
1119
|
return currentPageCount + addPage;
|
|
1097
1120
|
} else {
|
|
1121
|
+
pageGapTracker.delete(editorDom);
|
|
1098
1122
|
const allBreaksAfterLastElement = Array.from(paginationElement.querySelectorAll(".breaker"));
|
|
1099
1123
|
const allBreaksAfterLastElementRect = allBreaksAfterLastElement.filter((element) => element.getBoundingClientRect().top > lastElementRect.bottom);
|
|
1100
1124
|
const removePage = allBreaksAfterLastElementRect.length;
|
|
@@ -1221,7 +1245,7 @@ function createDecoration(pageOptions, headerHeightMap, footerHeightMap) {
|
|
|
1221
1245
|
return [pageWidget, firstHeaderWidget];
|
|
1222
1246
|
}
|
|
1223
1247
|
|
|
1224
|
-
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.
|
|
1248
|
+
// ../../node_modules/.pnpm/tiptap-pagination-plus@3.1.0_patch_hash=4857789ba5672a185fb96bdd854e03e0c6ec8ed26abfeea_16730b2b361cd3f71113c52459ed62fd/node_modules/tiptap-pagination-plus/dist/constants.js
|
|
1225
1249
|
var A4_PAGE_SIZE = getPageSize(1123, 794, 95, 95, 76, 76);
|
|
1226
1250
|
var A3_PAGE_SIZE = getPageSize(1591, 1123, 95, 95, 76, 76);
|
|
1227
1251
|
var A5_PAGE_SIZE = getPageSize(794, 419, 76, 76, 57, 57);
|
|
@@ -1419,41 +1443,150 @@ function createPerformanceMonitor(options) {
|
|
|
1419
1443
|
return monitor;
|
|
1420
1444
|
}
|
|
1421
1445
|
|
|
1422
|
-
// src/
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1446
|
+
// src/pasteNormalization.ts
|
|
1447
|
+
var KEEP_STYLE_PROPS = /* @__PURE__ */ new Set(["color", "background-color"]);
|
|
1448
|
+
var REMOVE_TAGS = /* @__PURE__ */ new Set([
|
|
1449
|
+
"meta",
|
|
1450
|
+
"style",
|
|
1451
|
+
"link",
|
|
1452
|
+
"base",
|
|
1453
|
+
"title",
|
|
1454
|
+
"script",
|
|
1455
|
+
"iframe",
|
|
1456
|
+
"object",
|
|
1457
|
+
"embed",
|
|
1458
|
+
"form",
|
|
1459
|
+
"input",
|
|
1460
|
+
"button",
|
|
1461
|
+
"select",
|
|
1462
|
+
"textarea",
|
|
1463
|
+
"noscript",
|
|
1464
|
+
// Word Office namespace dump paragraph
|
|
1465
|
+
"o:p"
|
|
1466
|
+
]);
|
|
1467
|
+
var KEEP_ATTRS = {
|
|
1468
|
+
a: /* @__PURE__ */ new Set(["href", "style"]),
|
|
1469
|
+
img: /* @__PURE__ */ new Set(["src", "alt", "style"]),
|
|
1470
|
+
td: /* @__PURE__ */ new Set(["colspan", "rowspan", "style"]),
|
|
1471
|
+
th: /* @__PURE__ */ new Set(["colspan", "rowspan", "style"]),
|
|
1472
|
+
col: /* @__PURE__ */ new Set(["span"]),
|
|
1473
|
+
"*": /* @__PURE__ */ new Set(["style"])
|
|
1474
|
+
};
|
|
1475
|
+
function detectClipboardSource(html) {
|
|
1476
|
+
if (/id\s*=\s*["']docs-internal/i.test(html)) return "google-docs";
|
|
1477
|
+
if (/<o:p[\s>]|xmlns(:w|:o)?=|class\s*=\s*["']?Mso|mso-/i.test(html)) return "word";
|
|
1478
|
+
return "generic";
|
|
1479
|
+
}
|
|
1480
|
+
function parseStyleDeclarations(style) {
|
|
1481
|
+
const map = /* @__PURE__ */ new Map();
|
|
1482
|
+
for (const decl of style.split(";")) {
|
|
1483
|
+
const idx = decl.indexOf(":");
|
|
1484
|
+
if (idx < 0) continue;
|
|
1485
|
+
const prop = decl.slice(0, idx).trim().toLowerCase();
|
|
1486
|
+
const val = decl.slice(idx + 1).trim();
|
|
1487
|
+
if (prop && val) map.set(prop, val);
|
|
1488
|
+
}
|
|
1489
|
+
return map;
|
|
1490
|
+
}
|
|
1491
|
+
function cleanStyleAttr(el) {
|
|
1492
|
+
const style = el.getAttribute("style");
|
|
1493
|
+
if (!style) return;
|
|
1494
|
+
const kept = [];
|
|
1495
|
+
parseStyleDeclarations(style).forEach((val, prop) => {
|
|
1496
|
+
if (KEEP_STYLE_PROPS.has(prop)) kept.push(`${prop}: ${val}`);
|
|
1497
|
+
});
|
|
1498
|
+
if (kept.length) el.setAttribute("style", kept.join("; "));
|
|
1499
|
+
else el.removeAttribute("style");
|
|
1500
|
+
}
|
|
1501
|
+
function cleanAttrs(el) {
|
|
1502
|
+
const keep = /* @__PURE__ */ new Set([...KEEP_ATTRS["*"] ?? [], ...KEEP_ATTRS[el.tagName.toLowerCase()] ?? []]);
|
|
1503
|
+
for (const attr of Array.from(el.attributes)) {
|
|
1504
|
+
if (!keep.has(attr.name)) el.removeAttribute(attr.name);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
function unwrap(el) {
|
|
1508
|
+
const parent = el.parentNode;
|
|
1509
|
+
if (!parent) return;
|
|
1510
|
+
while (el.firstChild) parent.insertBefore(el.firstChild, el);
|
|
1511
|
+
parent.removeChild(el);
|
|
1512
|
+
}
|
|
1513
|
+
function convertHighlightSpan(span, styles) {
|
|
1514
|
+
const bg = styles.get("background-color") ?? styles.get("background");
|
|
1515
|
+
if (!bg) return;
|
|
1516
|
+
const color = styles.get("color");
|
|
1517
|
+
const mark = span.ownerDocument.createElement("mark");
|
|
1518
|
+
mark.setAttribute("style", `background-color: ${bg}`);
|
|
1519
|
+
if (color) {
|
|
1520
|
+
const inner = span.ownerDocument.createElement("span");
|
|
1521
|
+
inner.setAttribute("style", `color: ${color}`);
|
|
1522
|
+
while (span.firstChild) inner.appendChild(span.firstChild);
|
|
1523
|
+
mark.appendChild(inner);
|
|
1524
|
+
} else {
|
|
1525
|
+
while (span.firstChild) mark.appendChild(span.firstChild);
|
|
1526
|
+
}
|
|
1527
|
+
span.replaceWith(mark);
|
|
1528
|
+
}
|
|
1529
|
+
function normalizeElement(el, source) {
|
|
1530
|
+
if (el.tagName === "SPAN") {
|
|
1531
|
+
const styles = parseStyleDeclarations(el.getAttribute("style") ?? "");
|
|
1532
|
+
if (styles.has("background-color") || styles.has("background")) {
|
|
1533
|
+
convertHighlightSpan(el, styles);
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
cleanStyleAttr(el);
|
|
1537
|
+
cleanAttrs(el);
|
|
1538
|
+
if (el.attributes.length === 0) unwrap(el);
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
if (el.tagName === "B") {
|
|
1542
|
+
const styles = parseStyleDeclarations(el.getAttribute("style") ?? "");
|
|
1543
|
+
if (styles.get("font-weight")?.replace(/\s/g, "") === "normal") {
|
|
1544
|
+
unwrap(el);
|
|
1545
|
+
return;
|
|
1447
1546
|
}
|
|
1448
1547
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1548
|
+
cleanStyleAttr(el);
|
|
1549
|
+
cleanAttrs(el);
|
|
1550
|
+
if (source === "word") {
|
|
1551
|
+
el.removeAttribute("class");
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
function removeComments(root) {
|
|
1555
|
+
const walker = root.ownerDocument.createTreeWalker(
|
|
1556
|
+
root,
|
|
1557
|
+
128
|
|
1558
|
+
/* NodeFilter.SHOW_COMMENT */
|
|
1452
1559
|
);
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1560
|
+
const comments = [];
|
|
1561
|
+
let node = walker.nextNode();
|
|
1562
|
+
while (node) {
|
|
1563
|
+
comments.push(node);
|
|
1564
|
+
node = walker.nextNode();
|
|
1565
|
+
}
|
|
1566
|
+
for (const c of comments) c.parentNode?.removeChild(c);
|
|
1456
1567
|
}
|
|
1568
|
+
function normalizeClipboardHTML(html) {
|
|
1569
|
+
if (!html || typeof html !== "string") return html ?? "";
|
|
1570
|
+
if (!/[<][a-zA-Z]/.test(html)) return html;
|
|
1571
|
+
const source = detectClipboardSource(html);
|
|
1572
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
1573
|
+
removeComments(doc.body);
|
|
1574
|
+
for (const tag of REMOVE_TAGS) {
|
|
1575
|
+
for (const el of Array.from(doc.body.getElementsByTagName(tag))) {
|
|
1576
|
+
el.parentNode?.removeChild(el);
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
const walk = (node) => {
|
|
1580
|
+
if (node.nodeType !== 1) return;
|
|
1581
|
+
for (const child of Array.from(node.childNodes)) walk(child);
|
|
1582
|
+
normalizeElement(node, source);
|
|
1583
|
+
};
|
|
1584
|
+
for (const child of Array.from(doc.body.childNodes)) walk(child);
|
|
1585
|
+
return doc.body.innerHTML;
|
|
1586
|
+
}
|
|
1587
|
+
var sanitizePastedHTML = normalizeClipboardHTML;
|
|
1588
|
+
|
|
1589
|
+
// src/Editor.ts
|
|
1457
1590
|
function migrateContent(content) {
|
|
1458
1591
|
if (!content) return content;
|
|
1459
1592
|
if (typeof content === "string") {
|
|
@@ -2151,11 +2284,13 @@ export {
|
|
|
2151
2284
|
createEditor,
|
|
2152
2285
|
createPerformanceMonitor,
|
|
2153
2286
|
definePlugin,
|
|
2287
|
+
detectClipboardSource,
|
|
2154
2288
|
findMatches,
|
|
2155
2289
|
getSearchState,
|
|
2156
2290
|
httpKeyStorage,
|
|
2157
2291
|
localStorageKeyStorage,
|
|
2158
2292
|
memoryKeyStorage,
|
|
2293
|
+
normalizeClipboardHTML,
|
|
2159
2294
|
openaiCompatibleProvider,
|
|
2160
2295
|
replaceAll,
|
|
2161
2296
|
replaceCurrent,
|