docxodus 9.1.0 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -18
- package/dist/editor-reconcile.d.ts.map +1 -1
- package/dist/editor-reconcile.js +33 -5
- package/dist/editor-reconcile.js.map +1 -1
- package/dist/editor.bundle.js +1912 -24
- package/dist/editor.d.ts +52 -3
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +261 -18
- package/dist/editor.js.map +1 -1
- package/dist/embed.bundle.js +1955 -22
- package/dist/embed.d.ts +26 -1
- package/dist/embed.d.ts.map +1 -1
- package/dist/embed.iife.js +1955 -22
- package/dist/embed.js +70 -1
- package/dist/embed.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/ribbon-chrome.d.ts +45 -0
- package/dist/ribbon-chrome.d.ts.map +1 -0
- package/dist/ribbon-chrome.js +965 -0
- package/dist/ribbon-chrome.js.map +1 -0
- package/dist/ribbon.d.ts +153 -0
- package/dist/ribbon.d.ts.map +1 -0
- package/dist/ribbon.js +830 -0
- package/dist/ribbon.js.map +1 -0
- package/dist/wasm/_framework/Docxodus.wasm +0 -0
- package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.boot.js +3 -3
- package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
- package/package.json +3 -3
package/dist/editor.d.ts
CHANGED
|
@@ -139,6 +139,15 @@ export declare class DocxEditor {
|
|
|
139
139
|
* block, and cleared when a caret is collapsed inside a block (so it never goes stale).
|
|
140
140
|
*/
|
|
141
141
|
private lastSelection;
|
|
142
|
+
/**
|
|
143
|
+
* Stable bookmark for a selection spanning independent editable blocks. Native controls such as
|
|
144
|
+
* a font-size combobox can take focus and collapse the live DOM selection; block ids + content
|
|
145
|
+
* offsets let the command restore that same range before applying. This is the multi-block
|
|
146
|
+
* counterpart to lastSelection above.
|
|
147
|
+
*/
|
|
148
|
+
private lastCrossBlockSelection;
|
|
149
|
+
/** State for the mouse-selection bridge between independent contenteditable block hosts. */
|
|
150
|
+
private dragSelection;
|
|
142
151
|
/** The docked header/footer bands, when `options.headerFooter` is on. */
|
|
143
152
|
private region;
|
|
144
153
|
/** Why the last reconcile() fell back to a full remount (null = it patched). For
|
|
@@ -147,6 +156,27 @@ export declare class DocxEditor {
|
|
|
147
156
|
private constructor();
|
|
148
157
|
/** Track the last meaningful selection so focus-stealing toolbar controls can still target it. */
|
|
149
158
|
private readonly onSelectionChange;
|
|
159
|
+
/** Start tracking a normal primary-button text drag inside one editable block. */
|
|
160
|
+
private readonly onMouseDown;
|
|
161
|
+
/**
|
|
162
|
+
* Apply the latest cross-block endpoint after the browser finishes its native mousemove
|
|
163
|
+
* selection update. Firefox rewrites Selection back into the originating contenteditable after
|
|
164
|
+
* event dispatch even when mousemove is cancelled; writing in requestAnimationFrame wins that
|
|
165
|
+
* race and happens before paint. Coalescing also avoids rebuilding a Range for every raw pointer
|
|
166
|
+
* event when the mouse is moving faster than the display can refresh.
|
|
167
|
+
*/
|
|
168
|
+
private queueDragSelection;
|
|
169
|
+
/** Cancel a queued repaint and discard the current gesture. */
|
|
170
|
+
private clearDragSelection;
|
|
171
|
+
/**
|
|
172
|
+
* Browsers fence native mouse selection at a contenteditable host boundary. Once a drag reaches
|
|
173
|
+
* another block in the same OOXML story, take over just that gesture and create the cross-block
|
|
174
|
+
* Selection the editor's existing multi-block command path consumes. Intra-block selection stays
|
|
175
|
+
* entirely native, and separate hosts remain intact for safe per-anchor commits.
|
|
176
|
+
*/
|
|
177
|
+
private readonly onMouseMove;
|
|
178
|
+
/** Commit the final cross-block endpoint before releasing the gesture state. */
|
|
179
|
+
private readonly onMouseUp;
|
|
150
180
|
/** The editable block (contenteditable [data-anchor]) containing `node`, if any, within this editor.
|
|
151
181
|
* Fenced by `container`, not `editRoot`, so header/footer band blocks — which live outside the
|
|
152
182
|
* body edit root by design — also register. The fence still rejects other editors on the page. */
|
|
@@ -188,6 +218,14 @@ export declare class DocxEditor {
|
|
|
188
218
|
setPaginated(value: boolean): void;
|
|
189
219
|
/** The editor's current DOM (for inspection/tests). */
|
|
190
220
|
get root(): HTMLElement;
|
|
221
|
+
/**
|
|
222
|
+
* The live `DocxSession` handle backing this editor — the model of record.
|
|
223
|
+
*
|
|
224
|
+
* Surfaced because chrome around the editor (the anchor rail) reports it as engine
|
|
225
|
+
* state, and reaching into the private field from a host page only worked because
|
|
226
|
+
* the bundle erases TypeScript's visibility.
|
|
227
|
+
*/
|
|
228
|
+
get sessionHandle(): number;
|
|
191
229
|
private assertOpen;
|
|
192
230
|
/**
|
|
193
231
|
* Rebuild unid → full-anchor-id from the live session projection.
|
|
@@ -266,9 +304,20 @@ export declare class DocxEditor {
|
|
|
266
304
|
private syncBlock;
|
|
267
305
|
/** Render a block by anchor and parse it into a detached element (null on error). */
|
|
268
306
|
private renderInto;
|
|
307
|
+
/** Render two blocks in ONE batched bridge call when the bundle carries RenderBlocksHtml —
|
|
308
|
+
* the per-render shell/converter setup is paid once instead of twice, which matters on the
|
|
309
|
+
* Enter path (split renders both halves synchronously under the keystroke). Falls back to
|
|
310
|
+
* two per-block renders on older bundles. Output is renderInto-identical per block (both
|
|
311
|
+
* routes share the same extraction). */
|
|
312
|
+
private renderTwo;
|
|
269
313
|
/** The editable block immediately before `el` within its own root, or null. */
|
|
270
314
|
private previousEditable;
|
|
271
315
|
private parseEdit;
|
|
316
|
+
/**
|
|
317
|
+
* Restore a cross-block selection after a native toolbar control took focus. The bookmark uses
|
|
318
|
+
* stable anchor ids and content offsets, so it also survives incremental block swaps.
|
|
319
|
+
*/
|
|
320
|
+
private restoreCrossBlockSelection;
|
|
272
321
|
/** Editable blocks the current selection covers, in document order. Uses Range.comparePoint
|
|
273
322
|
* (robust to a selection boundary that normalized onto a wrapper element rather than a block
|
|
274
323
|
* or text node — Range.intersectsNode misses the end block at a `(block, childCount)` boundary).
|
|
@@ -488,9 +537,9 @@ export declare class DocxEditor {
|
|
|
488
537
|
/** The `[data-anchor]` element of a fresh render root (the root itself for a leaf
|
|
489
538
|
* block, its descendant for a wrapper-shaped render like a table's align div). */
|
|
490
539
|
private static anchorElOf;
|
|
491
|
-
/** Insert/remove/swap body unit nodes per the diff. Returns
|
|
492
|
-
* ambiguity, order violation, wrapper semantics) — the
|
|
493
|
-
* so bailing just means a full repaint. */
|
|
540
|
+
/** Insert/remove/swap body unit nodes per the diff. Returns `true` on success or a
|
|
541
|
+
* bail-reason string (parent ambiguity, order violation, wrapper semantics) — the
|
|
542
|
+
* session is already correct, so bailing just means a full repaint. */
|
|
494
543
|
private applyBodyDiff;
|
|
495
544
|
/** Wire a freshly rendered unit root (and its nested blocks) and stamp the unit's
|
|
496
545
|
* content signature on its `[data-anchor]` element — the element the next
|
package/dist/editor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"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"}
|
package/dist/editor.js
CHANGED
|
@@ -306,6 +306,48 @@ function contentPositionIn(el, offset) {
|
|
|
306
306
|
return { node: lastText, offset: (lastText.textContent ?? "").length };
|
|
307
307
|
return { node: el, offset: el.childNodes.length };
|
|
308
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Resolve a viewport coordinate to a DOM caret. Firefox exposes the standards-track
|
|
311
|
+
* caretPositionFromPoint; Chromium/WebKit expose caretRangeFromPoint. Keeping the compatibility
|
|
312
|
+
* shim here lets the editor bridge native mouse selection across its intentionally-independent
|
|
313
|
+
* per-paragraph editing hosts without changing their commit boundaries.
|
|
314
|
+
*/
|
|
315
|
+
function caretPointFromClient(doc, clientX, clientY) {
|
|
316
|
+
const caretDoc = doc;
|
|
317
|
+
const position = caretDoc.caretPositionFromPoint?.(clientX, clientY);
|
|
318
|
+
if (position)
|
|
319
|
+
return { node: position.offsetNode, offset: position.offset };
|
|
320
|
+
const range = caretDoc.caretRangeFromPoint?.(clientX, clientY);
|
|
321
|
+
return range ? { node: range.startContainer, offset: range.startOffset } : null;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Set a possibly-backward pair of points as one normalized DOM Range. Chromium's
|
|
325
|
+
* Selection.setBaseAndExtent() exposes cross-contenteditable endpoints but clips toString() and
|
|
326
|
+
* the effective contents to the originating host; addRange() carries the complete cross-block
|
|
327
|
+
* contents. Direction is immaterial to editor commands, which consume normalized start/end.
|
|
328
|
+
*/
|
|
329
|
+
function setSelectionBetween(anchor, focus) {
|
|
330
|
+
const sel = typeof window !== "undefined" ? window.getSelection() : null;
|
|
331
|
+
if (!sel || !anchor.node.isConnected || !focus.node.isConnected)
|
|
332
|
+
return false;
|
|
333
|
+
try {
|
|
334
|
+
const probe = document.createRange();
|
|
335
|
+
probe.setStart(anchor.node, anchor.offset);
|
|
336
|
+
probe.collapse(true);
|
|
337
|
+
const focusIsBefore = probe.comparePoint(focus.node, focus.offset) < 0;
|
|
338
|
+
const range = document.createRange();
|
|
339
|
+
const start = focusIsBefore ? focus : anchor;
|
|
340
|
+
const end = focusIsBefore ? anchor : focus;
|
|
341
|
+
range.setStart(start.node, start.offset);
|
|
342
|
+
range.setEnd(end.node, end.offset);
|
|
343
|
+
sel.removeAllRanges();
|
|
344
|
+
sel.addRange(range);
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
309
351
|
/** Place the caret at content offset `offset` within `el`, skipping marker text. */
|
|
310
352
|
function placeCaretAtOffset(el, offset) {
|
|
311
353
|
const sel = typeof window !== "undefined" ? window.getSelection() : null;
|
|
@@ -472,6 +514,15 @@ export class DocxEditor {
|
|
|
472
514
|
* block, and cleared when a caret is collapsed inside a block (so it never goes stale).
|
|
473
515
|
*/
|
|
474
516
|
this.lastSelection = null;
|
|
517
|
+
/**
|
|
518
|
+
* Stable bookmark for a selection spanning independent editable blocks. Native controls such as
|
|
519
|
+
* a font-size combobox can take focus and collapse the live DOM selection; block ids + content
|
|
520
|
+
* offsets let the command restore that same range before applying. This is the multi-block
|
|
521
|
+
* counterpart to lastSelection above.
|
|
522
|
+
*/
|
|
523
|
+
this.lastCrossBlockSelection = null;
|
|
524
|
+
/** State for the mouse-selection bridge between independent contenteditable block hosts. */
|
|
525
|
+
this.dragSelection = null;
|
|
475
526
|
/** The docked header/footer bands, when `options.headerFooter` is on. */
|
|
476
527
|
this.region = null;
|
|
477
528
|
/** Why the last reconcile() fell back to a full remount (null = it patched). For
|
|
@@ -485,27 +536,143 @@ export class DocxEditor {
|
|
|
485
536
|
if (!sel || sel.rangeCount === 0)
|
|
486
537
|
return; // no selection info — keep the cache as-is
|
|
487
538
|
const range = sel.getRangeAt(0);
|
|
488
|
-
const
|
|
539
|
+
const startBlock = this.editableBlockOf(range.startContainer);
|
|
540
|
+
const endBlock = this.editableBlockOf(range.endContainer);
|
|
541
|
+
const block = startBlock ?? endBlock;
|
|
489
542
|
if (!block)
|
|
490
543
|
return; // selection is outside the editor (e.g. a toolbar field) — keep the cache
|
|
491
|
-
const unid = block.getAttribute("data-anchor");
|
|
492
|
-
if (!unid)
|
|
493
|
-
return;
|
|
494
544
|
if (range.collapsed) {
|
|
495
545
|
this.lastSelection = null; // an explicit caret in a block — drop any stale selection
|
|
546
|
+
this.lastCrossBlockSelection = null;
|
|
496
547
|
return;
|
|
497
548
|
}
|
|
549
|
+
if (startBlock && endBlock && startBlock !== endBlock &&
|
|
550
|
+
this.ownerRoot(startBlock) === this.ownerRoot(endBlock)) {
|
|
551
|
+
const startUnid = startBlock.getAttribute("data-anchor");
|
|
552
|
+
const endUnid = endBlock.getAttribute("data-anchor");
|
|
553
|
+
if (startUnid && endUnid) {
|
|
554
|
+
this.lastSelection = null;
|
|
555
|
+
this.lastCrossBlockSelection = {
|
|
556
|
+
startUnid,
|
|
557
|
+
startOffset: contentOffsetOf(startBlock, range.startContainer, range.startOffset),
|
|
558
|
+
endUnid,
|
|
559
|
+
endOffset: contentOffsetOf(endBlock, range.endContainer, range.endOffset),
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const unid = block.getAttribute("data-anchor");
|
|
565
|
+
if (!unid)
|
|
566
|
+
return;
|
|
567
|
+
this.lastCrossBlockSelection = null;
|
|
498
568
|
const span = selectionSpanIn(block);
|
|
499
569
|
if (span)
|
|
500
570
|
this.lastSelection = { unid, span };
|
|
501
571
|
};
|
|
572
|
+
/** Start tracking a normal primary-button text drag inside one editable block. */
|
|
573
|
+
this.onMouseDown = (event) => {
|
|
574
|
+
if (this.closed || !this.options.editable || event.button !== 0)
|
|
575
|
+
return;
|
|
576
|
+
const target = event.target instanceof Node ? event.target : null;
|
|
577
|
+
const origin = this.editableBlockOf(target);
|
|
578
|
+
if (!origin || isInMarker(target))
|
|
579
|
+
return;
|
|
580
|
+
const anchor = caretPointFromClient(document, event.clientX, event.clientY);
|
|
581
|
+
if (!anchor || this.editableBlockOf(anchor.node) !== origin)
|
|
582
|
+
return;
|
|
583
|
+
this.clearDragSelection();
|
|
584
|
+
this.dragSelection = {
|
|
585
|
+
anchor,
|
|
586
|
+
origin,
|
|
587
|
+
crossedBlockBoundary: false,
|
|
588
|
+
focus: null,
|
|
589
|
+
frame: null,
|
|
590
|
+
};
|
|
591
|
+
};
|
|
592
|
+
/**
|
|
593
|
+
* Browsers fence native mouse selection at a contenteditable host boundary. Once a drag reaches
|
|
594
|
+
* another block in the same OOXML story, take over just that gesture and create the cross-block
|
|
595
|
+
* Selection the editor's existing multi-block command path consumes. Intra-block selection stays
|
|
596
|
+
* entirely native, and separate hosts remain intact for safe per-anchor commits.
|
|
597
|
+
*/
|
|
598
|
+
this.onMouseMove = (event) => {
|
|
599
|
+
const drag = this.dragSelection;
|
|
600
|
+
if (!drag)
|
|
601
|
+
return;
|
|
602
|
+
if ((event.buttons & 1) === 0) {
|
|
603
|
+
this.clearDragSelection();
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
const focus = caretPointFromClient(document, event.clientX, event.clientY);
|
|
607
|
+
if (!focus || isInMarker(focus.node))
|
|
608
|
+
return;
|
|
609
|
+
const focusBlock = this.editableBlockOf(focus.node);
|
|
610
|
+
if (!focusBlock || this.ownerRoot(focusBlock) !== this.ownerRoot(drag.origin))
|
|
611
|
+
return;
|
|
612
|
+
if (focusBlock !== drag.origin)
|
|
613
|
+
drag.crossedBlockBoundary = true;
|
|
614
|
+
if (!drag.crossedBlockBoundary)
|
|
615
|
+
return;
|
|
616
|
+
event.preventDefault();
|
|
617
|
+
this.queueDragSelection(drag, focus);
|
|
618
|
+
};
|
|
619
|
+
/** Commit the final cross-block endpoint before releasing the gesture state. */
|
|
620
|
+
this.onMouseUp = (event) => {
|
|
621
|
+
const drag = this.dragSelection;
|
|
622
|
+
if (!drag)
|
|
623
|
+
return;
|
|
624
|
+
if (drag.crossedBlockBoundary) {
|
|
625
|
+
const focus = caretPointFromClient(document, event.clientX, event.clientY);
|
|
626
|
+
const focusBlock = focus ? this.editableBlockOf(focus.node) : null;
|
|
627
|
+
if (focus && focusBlock && this.ownerRoot(focusBlock) === this.ownerRoot(drag.origin)) {
|
|
628
|
+
event.preventDefault();
|
|
629
|
+
setSelectionBetween(drag.anchor, focus);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
this.clearDragSelection();
|
|
633
|
+
};
|
|
502
634
|
this.container = container;
|
|
503
635
|
this.exports = exports;
|
|
504
636
|
this.handle = handle;
|
|
505
637
|
this.options = options;
|
|
506
638
|
this.editRoot = container;
|
|
507
|
-
if (typeof document !== "undefined")
|
|
639
|
+
if (typeof document !== "undefined") {
|
|
508
640
|
document.addEventListener("selectionchange", this.onSelectionChange);
|
|
641
|
+
document.addEventListener("mousedown", this.onMouseDown, true);
|
|
642
|
+
document.addEventListener("mousemove", this.onMouseMove, true);
|
|
643
|
+
document.addEventListener("mouseup", this.onMouseUp, true);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Apply the latest cross-block endpoint after the browser finishes its native mousemove
|
|
648
|
+
* selection update. Firefox rewrites Selection back into the originating contenteditable after
|
|
649
|
+
* event dispatch even when mousemove is cancelled; writing in requestAnimationFrame wins that
|
|
650
|
+
* race and happens before paint. Coalescing also avoids rebuilding a Range for every raw pointer
|
|
651
|
+
* event when the mouse is moving faster than the display can refresh.
|
|
652
|
+
*/
|
|
653
|
+
queueDragSelection(drag, focus) {
|
|
654
|
+
drag.focus = focus;
|
|
655
|
+
if (drag.frame !== null)
|
|
656
|
+
return;
|
|
657
|
+
const view = this.container.ownerDocument.defaultView;
|
|
658
|
+
if (!view) {
|
|
659
|
+
setSelectionBetween(drag.anchor, focus);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
drag.frame = view.requestAnimationFrame(() => {
|
|
663
|
+
drag.frame = null;
|
|
664
|
+
if (this.dragSelection !== drag || !drag.focus)
|
|
665
|
+
return;
|
|
666
|
+
setSelectionBetween(drag.anchor, drag.focus);
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
/** Cancel a queued repaint and discard the current gesture. */
|
|
670
|
+
clearDragSelection() {
|
|
671
|
+
const drag = this.dragSelection;
|
|
672
|
+
if (drag && drag.frame !== null) {
|
|
673
|
+
this.container.ownerDocument.defaultView?.cancelAnimationFrame(drag.frame);
|
|
674
|
+
}
|
|
675
|
+
this.dragSelection = null;
|
|
509
676
|
}
|
|
510
677
|
/** The editable block (contenteditable [data-anchor]) containing `node`, if any, within this editor.
|
|
511
678
|
* Fenced by `container`, not `editRoot`, so header/footer band blocks — which live outside the
|
|
@@ -596,8 +763,13 @@ export class DocxEditor {
|
|
|
596
763
|
if (this.closed)
|
|
597
764
|
return;
|
|
598
765
|
this.closed = true;
|
|
599
|
-
if (typeof document !== "undefined")
|
|
766
|
+
if (typeof document !== "undefined") {
|
|
600
767
|
document.removeEventListener("selectionchange", this.onSelectionChange);
|
|
768
|
+
document.removeEventListener("mousedown", this.onMouseDown, true);
|
|
769
|
+
document.removeEventListener("mousemove", this.onMouseMove, true);
|
|
770
|
+
document.removeEventListener("mouseup", this.onMouseUp, true);
|
|
771
|
+
}
|
|
772
|
+
this.clearDragSelection();
|
|
601
773
|
this.exports.DocxSessionBridge.CloseSession(this.handle);
|
|
602
774
|
}
|
|
603
775
|
/**
|
|
@@ -616,6 +788,16 @@ export class DocxEditor {
|
|
|
616
788
|
get root() {
|
|
617
789
|
return this.container;
|
|
618
790
|
}
|
|
791
|
+
/**
|
|
792
|
+
* The live `DocxSession` handle backing this editor — the model of record.
|
|
793
|
+
*
|
|
794
|
+
* Surfaced because chrome around the editor (the anchor rail) reports it as engine
|
|
795
|
+
* state, and reaching into the private field from a host page only worked because
|
|
796
|
+
* the bundle erases TypeScript's visibility.
|
|
797
|
+
*/
|
|
798
|
+
get sessionHandle() {
|
|
799
|
+
return this.handle;
|
|
800
|
+
}
|
|
619
801
|
// ─── internals ───────────────────────────────────────────────────────
|
|
620
802
|
assertOpen() {
|
|
621
803
|
if (this.closed)
|
|
@@ -996,8 +1178,7 @@ export class DocxEditor {
|
|
|
996
1178
|
this.options.onEdit?.({ anchorId: second.id, unid: second.unid });
|
|
997
1179
|
return;
|
|
998
1180
|
}
|
|
999
|
-
const firstEl = this.
|
|
1000
|
-
const secondEl = this.renderInto(second.id);
|
|
1181
|
+
const [firstEl, secondEl] = this.renderTwo(first.id, second.id);
|
|
1001
1182
|
if (!firstEl || !secondEl)
|
|
1002
1183
|
return;
|
|
1003
1184
|
// el is the focused block — replaceNode guards the re-entrant blur→commit and tolerates a
|
|
@@ -1133,6 +1314,33 @@ export class DocxEditor {
|
|
|
1133
1314
|
return null;
|
|
1134
1315
|
return new DOMParser().parseFromString(html, "text/html").body.firstElementChild;
|
|
1135
1316
|
}
|
|
1317
|
+
/** Render two blocks in ONE batched bridge call when the bundle carries RenderBlocksHtml —
|
|
1318
|
+
* the per-render shell/converter setup is paid once instead of twice, which matters on the
|
|
1319
|
+
* Enter path (split renders both halves synchronously under the keystroke). Falls back to
|
|
1320
|
+
* two per-block renders on older bundles. Output is renderInto-identical per block (both
|
|
1321
|
+
* routes share the same extraction). */
|
|
1322
|
+
renderTwo(a, b) {
|
|
1323
|
+
const bridge = this.exports.DocxSessionBridge;
|
|
1324
|
+
if (typeof bridge.RenderBlocksHtml === "function") {
|
|
1325
|
+
try {
|
|
1326
|
+
const map = JSON.parse(bridge.RenderBlocksHtml(this.handle, JSON.stringify([a, b]), this.options.cssPrefix, this.options.fabricateClasses));
|
|
1327
|
+
if (!map.error) {
|
|
1328
|
+
const parse = (h) => h
|
|
1329
|
+
? new DOMParser().parseFromString(h, "text/html").body
|
|
1330
|
+
.firstElementChild
|
|
1331
|
+
: null;
|
|
1332
|
+
const fa = parse(map[a]);
|
|
1333
|
+
const fb = parse(map[b]);
|
|
1334
|
+
if (fa && fb)
|
|
1335
|
+
return [fa, fb];
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
catch {
|
|
1339
|
+
/* fall through to per-block renders */
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
return [this.renderInto(a), this.renderInto(b)];
|
|
1343
|
+
}
|
|
1136
1344
|
/** The editable block immediately before `el` within its own root, or null. */
|
|
1137
1345
|
previousEditable(el) {
|
|
1138
1346
|
const all = Array.from(this.ownerRoot(el).querySelectorAll('[data-anchor][contenteditable="true"]'));
|
|
@@ -1149,12 +1357,34 @@ export class DocxEditor {
|
|
|
1149
1357
|
}
|
|
1150
1358
|
// ─── M5: formatting commands (ribbon) ────────────────────────────────
|
|
1151
1359
|
// ─── Multi-block selection helpers (format a whole stack of paragraphs at once) ──────
|
|
1360
|
+
/**
|
|
1361
|
+
* Restore a cross-block selection after a native toolbar control took focus. The bookmark uses
|
|
1362
|
+
* stable anchor ids and content offsets, so it also survives incremental block swaps.
|
|
1363
|
+
*/
|
|
1364
|
+
restoreCrossBlockSelection() {
|
|
1365
|
+
const bookmark = this.lastCrossBlockSelection;
|
|
1366
|
+
const active = this.activeBlock;
|
|
1367
|
+
if (!bookmark || !active)
|
|
1368
|
+
return false;
|
|
1369
|
+
const all = Array.from(this.ownerRoot(active).querySelectorAll('[data-anchor][contenteditable="true"]'));
|
|
1370
|
+
const first = all.find((block) => block.getAttribute("data-anchor") === bookmark.startUnid);
|
|
1371
|
+
const last = all.find((block) => block.getAttribute("data-anchor") === bookmark.endUnid);
|
|
1372
|
+
if (!first || !last || first === last || all.indexOf(first) > all.indexOf(last))
|
|
1373
|
+
return false;
|
|
1374
|
+
const start = contentPositionIn(first, bookmark.startOffset);
|
|
1375
|
+
const end = contentPositionIn(last, bookmark.endOffset);
|
|
1376
|
+
return setSelectionBetween(start, end);
|
|
1377
|
+
}
|
|
1152
1378
|
/** Editable blocks the current selection covers, in document order. Uses Range.comparePoint
|
|
1153
1379
|
* (robust to a selection boundary that normalized onto a wrapper element rather than a block
|
|
1154
1380
|
* or text node — Range.intersectsNode misses the end block at a `(block, childCount)` boundary).
|
|
1155
1381
|
* A collapsed or single-block selection yields just the active block. */
|
|
1156
1382
|
selectedBlocks() {
|
|
1157
|
-
|
|
1383
|
+
let sel = typeof window !== "undefined" ? window.getSelection() : null;
|
|
1384
|
+
if ((!sel || sel.rangeCount === 0 || sel.isCollapsed) && this.lastCrossBlockSelection) {
|
|
1385
|
+
this.restoreCrossBlockSelection();
|
|
1386
|
+
sel = typeof window !== "undefined" ? window.getSelection() : null;
|
|
1387
|
+
}
|
|
1158
1388
|
// Enumerate the ACTIVE block's own root — a band's story container, else the body edit root —
|
|
1159
1389
|
// so a selection can never span a band and the body (different OOXML parts).
|
|
1160
1390
|
const root = this.activeBlock ? this.ownerRoot(this.activeBlock) : this.editRoot;
|
|
@@ -1992,8 +2222,9 @@ export class DocxEditor {
|
|
|
1992
2222
|
if (oldMarker !== newMarker)
|
|
1993
2223
|
return this.bail("substituted li marker drift");
|
|
1994
2224
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
2225
|
+
const bodyApply = this.applyBodyDiff(oldNodes, plan.body, bodyDiff, freshBody);
|
|
2226
|
+
if (bodyApply !== true)
|
|
2227
|
+
return this.bail(`applyBodyDiff bail: ${bodyApply}`);
|
|
1997
2228
|
this.applyNotesDiff("footnotes", plan.footnotes, fnState, rendered);
|
|
1998
2229
|
this.applyNotesDiff("endnotes", plan.endnotes, enState, rendered);
|
|
1999
2230
|
// Note chrome (marker sup text / hrefs / li values) is position-derived and NOT
|
|
@@ -2032,9 +2263,9 @@ export class DocxEditor {
|
|
|
2032
2263
|
? root
|
|
2033
2264
|
: root.querySelector("[data-anchor]");
|
|
2034
2265
|
}
|
|
2035
|
-
/** Insert/remove/swap body unit nodes per the diff. Returns
|
|
2036
|
-
* ambiguity, order violation, wrapper semantics) — the
|
|
2037
|
-
* so bailing just means a full repaint. */
|
|
2266
|
+
/** Insert/remove/swap body unit nodes per the diff. Returns `true` on success or a
|
|
2267
|
+
* bail-reason string (parent ambiguity, order violation, wrapper semantics) — the
|
|
2268
|
+
* session is already correct, so bailing just means a full repaint. */
|
|
2038
2269
|
applyBodyDiff(oldNodes, units, diff, fresh) {
|
|
2039
2270
|
// Kept nodes must appear in increasing old order (no move support in v1).
|
|
2040
2271
|
let lastOld = -1;
|
|
@@ -2043,7 +2274,7 @@ export class DocxEditor {
|
|
|
2043
2274
|
if (oi === undefined)
|
|
2044
2275
|
continue;
|
|
2045
2276
|
if (oi < lastOld)
|
|
2046
|
-
return
|
|
2277
|
+
return "kept order violation";
|
|
2047
2278
|
lastOld = oi;
|
|
2048
2279
|
}
|
|
2049
2280
|
// In-place substitutions first: replace at WRAPPER level so a table swaps with its
|
|
@@ -2059,8 +2290,20 @@ export class DocxEditor {
|
|
|
2059
2290
|
else if (oldWrapper === oldNodes[oi]) {
|
|
2060
2291
|
oldNodes[oi].replaceWith(freshRoot);
|
|
2061
2292
|
}
|
|
2293
|
+
else if (!oldNodes[oi].hasAttribute("data-render-sig") &&
|
|
2294
|
+
unidOf(units[nj].id) === oldNodes[oi].getAttribute("data-anchor")) {
|
|
2295
|
+
// Same block, leaf render, wrapped slot, and the old node carries NO signature:
|
|
2296
|
+
// a sig-less node is one a SINGLE-BLOCK swap placed (mount and reconcile both
|
|
2297
|
+
// stamp signatures), and those swaps never change border grouping — every
|
|
2298
|
+
// border-changing op forces a full remount. The wrapper is therefore still
|
|
2299
|
+
// valid; swap the leaf inside it, exactly as the single-block path itself does
|
|
2300
|
+
// for a bordered paragraph. A STAMPED wrapped slot whose content changed stays
|
|
2301
|
+
// remount territory — the change could be the border itself.
|
|
2302
|
+
oldNodes[oi].replaceWith(freshRoot);
|
|
2303
|
+
}
|
|
2062
2304
|
else {
|
|
2063
|
-
|
|
2305
|
+
// border-div semantics, remount
|
|
2306
|
+
return `leaf render into wrapped slot (unit ${units[nj].id.slice(0, 24)})`;
|
|
2064
2307
|
}
|
|
2065
2308
|
this.wireUnit(freshRoot, units[nj]);
|
|
2066
2309
|
}
|
|
@@ -2092,13 +2335,13 @@ export class DocxEditor {
|
|
|
2092
2335
|
const prevW = prev ? this.unitWrapperOf(prev) : null;
|
|
2093
2336
|
const nextW = next ? this.unitWrapperOf(next) : null;
|
|
2094
2337
|
if (prevW && nextW && prevW.parentElement !== nextW.parentElement)
|
|
2095
|
-
return
|
|
2338
|
+
return "insert neighbors in different parents";
|
|
2096
2339
|
if (prevW)
|
|
2097
2340
|
prevW.after(el);
|
|
2098
2341
|
else if (nextW)
|
|
2099
2342
|
nextW.before(el);
|
|
2100
2343
|
else
|
|
2101
|
-
return
|
|
2344
|
+
return "insert with no anchored neighbor"; // empty container — nowhere provably correct
|
|
2102
2345
|
this.wireUnit(el, units[j]);
|
|
2103
2346
|
}
|
|
2104
2347
|
// Pure removals last, taking now-empty generated wrappers with them.
|