smartrte-core 1.0.0-beta.5 → 1.0.0-beta.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0-beta.6
4
+
5
+ - Fix pasting a long, list-free document (many short paragraphs, no images) being slow enough to look broken — the Office/Google Docs list normalizers recursed into every paragraph hunting for nested lists a `<p>` can never structurally contain. Pasting a few thousand blocks now completes in well under a second instead of multiple seconds to tens of seconds. See `docs/bugs/clipboard-paste-slow-on-large-list-free-documents.md` for the full investigation, including a known remaining limitation for very large (10,000+ block) pastes.
6
+
3
7
  ## 1.0.0-beta.5
4
8
 
5
9
  - Add a per-block `lineHeight` attribute (paragraph, heading, blockquote, code block) — a unitless multiplier matching CSS `line-height`'s own unitless mode (`1`, `1.5`, `2`, ...), consistent with how `align`/`indentLevel` are represented. Absence means no override (the browser/font's own natural line-height), not a forced `1`. Full HTML round-trip fidelity via a dedicated `data-smart-line-height` marker — deliberately does **not** fall back to reading a bare CSS `line-height` value on import (unlike `align`/`indentLevel`'s own CSS fallbacks): real captured clipboard fixtures show `line-height` is frequently an ambient, whole-document default baked onto every paragraph, not a deliberate per-paragraph choice, and recognizing it would have silently misattributed that default as an explicit override on every pasted document. Real Word line spacing (`w:spacing`/`w:line`/`w:lineRule="auto"`) on DOCX export; declared `lossy` there (confirmed empirically that DOCX reimport via mammoth drops it) and `unsupported` (content preserved) in Markdown. Inherits `full` PDF fidelity for free, since this package's "Save as PDF" prints the same HTML export.
@@ -2,6 +2,20 @@ import { parseCanonicalListMarkdown, serializeCanonicalListHtml } from "../list/
2
2
  const listTags = new Set(["UL", "OL"]);
3
3
  const blockSelector = ":scope > p, :scope > h1, :scope > h2, :scope > h3, :scope > h4, :scope > h5, :scope > h6, :scope > ul, :scope > ol, :scope > blockquote, :scope > pre, :scope > table, :scope > div";
4
4
  const msoListPattern = /(?:^|;)\s*mso-list\s*:\s*([^\s;]+)\s+level(\d+)\s+([^\s;]+)/i;
5
+ /**
6
+ * Elements whose HTML content model is inline-only - a real HTML parser
7
+ * never lets a <p>/<ul>/<ol>/<div> nest inside one of these (an outer <p>
8
+ * auto-closes first), so a nested Office mso-list paragraph or a
9
+ * declared-level <ul>/<ol> can never appear inside one. normalizeMsoLists/
10
+ * normalizeDeclaredLevelLists below recurse looking for exactly those two
11
+ * things - recursing into one of these tags is always wasted work. See
12
+ * docs/bugs/clipboard-paste-normalizer-quadratic-blowup-on-large-documents.md:
13
+ * on a real document with thousands of top-level paragraphs (the common
14
+ * case for a long pasted document with no lists at all), skipping this
15
+ * recursion is what turns an O(n) visit count with an accidentally
16
+ * document-size-scaled per-call cost back into genuinely linear work.
17
+ */
18
+ const INLINE_LEAF_TAGS = new Set(["P", "SPAN", "B", "STRONG", "I", "EM", "U", "S", "A", "CODE", "BR", "IMG", "SUP", "SUB"]);
5
19
  const unwrap = (element) => element.replaceWith(...Array.from(element.childNodes));
6
20
  const normalizeTransparentContainers = (document) => {
7
21
  const docsWrapper = document.body.querySelector('[id^="docs-internal-guid-"]');
@@ -71,7 +85,8 @@ const normalizeMsoLists = (document) => {
71
85
  for (let index = 0; index < children.length;) {
72
86
  const first = msoListEntry(children[index]);
73
87
  if (!first) {
74
- visit(children[index]);
88
+ if (!INLINE_LEAF_TAGS.has(children[index].tagName))
89
+ visit(children[index]);
75
90
  index += 1;
76
91
  continue;
77
92
  }
@@ -167,7 +182,8 @@ const normalizeDeclaredLevelLists = (document) => {
167
182
  run[0].replaceWith(...roots);
168
183
  run.slice(1).forEach((list) => list.remove());
169
184
  }
170
- Array.from(parent.children).forEach((child) => visit(child));
185
+ Array.from(parent.children).forEach((child) => { if (!INLINE_LEAF_TAGS.has(child.tagName))
186
+ visit(child); });
171
187
  };
172
188
  visit(document.body);
173
189
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smartrte-core",
3
- "version": "1.0.0-beta.5",
3
+ "version": "1.0.0-beta.6",
4
4
  "description": "Framework-agnostic document model and editing primitives for Smart RTE",
5
5
  "repository": {
6
6
  "type": "git",