docxodus 12.1.0 → 12.3.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 +14 -4
- package/dist/editor.bundle.js +137 -23
- package/dist/editor.d.ts +25 -3
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +132 -13
- package/dist/editor.js.map +1 -1
- package/dist/embed.bundle.js +1163 -48
- package/dist/embed.d.ts +30 -1
- package/dist/embed.d.ts.map +1 -1
- package/dist/embed.iife.js +1163 -48
- package/dist/embed.js +124 -13
- package/dist/embed.js.map +1 -1
- package/dist/export-assets.json +33 -33
- package/dist/history-checkpoints.d.ts +48 -0
- package/dist/history-checkpoints.d.ts.map +1 -0
- package/dist/history-checkpoints.js +100 -0
- package/dist/history-checkpoints.js.map +1 -0
- package/dist/history-controls.d.ts +30 -0
- package/dist/history-controls.d.ts.map +1 -0
- package/dist/history-controls.js +333 -0
- package/dist/history-controls.js.map +1 -0
- package/dist/history-indexeddb.d.ts +12 -0
- package/dist/history-indexeddb.d.ts.map +1 -0
- package/dist/history-indexeddb.js +154 -0
- package/dist/history-indexeddb.js.map +1 -0
- package/dist/history.d.ts +265 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +307 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -2
- package/dist/index.js.map +1 -1
- package/dist/ribbon-chrome.d.ts +2 -2
- package/dist/ribbon-chrome.d.ts.map +1 -1
- package/dist/ribbon-chrome.js +6 -4
- package/dist/ribbon-chrome.js.map +1 -1
- package/dist/ribbon.js +42 -9
- package/dist/ribbon.js.map +1 -1
- package/dist/session.js +1 -1
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm +0 -0
- package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm.br +0 -0
- package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm +0 -0
- package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm.br +0 -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/System.Collections.Concurrent.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.Concurrent.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Packaging.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Packaging.wasm.br +0 -0
- package/dist/wasm/_framework/System.Linq.Expressions.wasm +0 -0
- package/dist/wasm/_framework/System.Linq.Expressions.wasm.br +0 -0
- package/dist/wasm/_framework/System.Memory.wasm +0 -0
- package/dist/wasm/_framework/System.Memory.wasm.br +0 -0
- package/dist/wasm/_framework/System.Net.Http.wasm +0 -0
- package/dist/wasm/_framework/System.Net.Http.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.wasm.br +0 -0
- package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm +0 -0
- package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm.br +0 -0
- package/dist/wasm/_framework/System.Runtime.wasm +0 -0
- package/dist/wasm/_framework/System.Runtime.wasm.br +0 -0
- package/dist/wasm/_framework/System.Security.Cryptography.wasm +0 -0
- package/dist/wasm/_framework/System.Security.Cryptography.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.Json.wasm +0 -0
- package/dist/wasm/_framework/System.Text.Json.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.boot.js +21 -21
- package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
- package/package.json +6 -3
package/dist/editor.js
CHANGED
|
@@ -614,16 +614,20 @@ function selectionSpanIn(block) {
|
|
|
614
614
|
const span = trimmedSpan(block, { start: Math.min(start, end), length: Math.abs(end - start) });
|
|
615
615
|
return span.length > 0 ? span : null;
|
|
616
616
|
}
|
|
617
|
-
/**
|
|
618
|
-
|
|
619
|
-
|
|
617
|
+
/**
|
|
618
|
+
* A DOM Range over the content text spanning [start, start+length) within `el` (skips markers),
|
|
619
|
+
* or null when the block is detached or the offsets do not resolve. Building the range is kept
|
|
620
|
+
* separate from selecting it so a caller that only wants to PAINT a span (find preview) does not
|
|
621
|
+
* have to touch the selection — and therefore focus — at all.
|
|
622
|
+
*/
|
|
623
|
+
function contentRangeIn(el, start, length) {
|
|
620
624
|
// The block may have been swapped out of the document by a re-render before this runs
|
|
621
625
|
// (e.g. a focus-stealing toolbar control firing twice). addRange on a detached range
|
|
622
626
|
// throws "the given range isn't in document" — skip rather than warn.
|
|
623
|
-
if (!
|
|
624
|
-
return;
|
|
625
|
-
el.
|
|
626
|
-
const range =
|
|
627
|
+
if (!el.isConnected)
|
|
628
|
+
return null;
|
|
629
|
+
const doc = el.ownerDocument;
|
|
630
|
+
const range = doc.createRange();
|
|
627
631
|
const from = contentPositionIn(el, start);
|
|
628
632
|
const to = contentPositionIn(el, start + length);
|
|
629
633
|
try {
|
|
@@ -631,11 +635,66 @@ function selectRange(el, start, length) {
|
|
|
631
635
|
range.setEnd(to.node, to.offset);
|
|
632
636
|
}
|
|
633
637
|
catch {
|
|
634
|
-
return;
|
|
638
|
+
return null;
|
|
635
639
|
}
|
|
640
|
+
return range;
|
|
641
|
+
}
|
|
642
|
+
/** Restore a content-text selection spanning [start, start+length) within `el` (skips markers). */
|
|
643
|
+
function selectRange(el, start, length) {
|
|
644
|
+
const sel = typeof window !== "undefined" ? window.getSelection() : null;
|
|
645
|
+
if (!sel || !el.isConnected)
|
|
646
|
+
return;
|
|
647
|
+
el.focus();
|
|
648
|
+
const range = contentRangeIn(el, start, length);
|
|
649
|
+
if (!range)
|
|
650
|
+
return;
|
|
636
651
|
sel.removeAllRanges();
|
|
637
652
|
sel.addRange(range);
|
|
638
653
|
}
|
|
654
|
+
// ─── Find painting ──────────────────────────────────────────────────────────
|
|
655
|
+
//
|
|
656
|
+
// A find box has to show WHERE the hits are while the user is still typing into it, so it cannot
|
|
657
|
+
// express "the current match" as a selection: putting a caret in a contenteditable block focuses
|
|
658
|
+
// that block, and the next keystroke lands in the document instead of the search field. These
|
|
659
|
+
// highlights paint the same information with no focus and no DOM mutation — the CSS Custom
|
|
660
|
+
// Highlight API takes plain Ranges over the live text and styles them from the stylesheet.
|
|
661
|
+
/** Every match currently on screen. */
|
|
662
|
+
const FIND_HIGHLIGHT = "docxodus-find";
|
|
663
|
+
/** The one the find bar's counter is pointing at. */
|
|
664
|
+
const FIND_HIGHLIGHT_ACTIVE = "docxodus-find-active";
|
|
665
|
+
/** Ranges are cheap, but a pathological query ("e" in a book) is not worth painting in full. */
|
|
666
|
+
const FIND_PAINT_LIMIT = 500;
|
|
667
|
+
const findHighlightStyledDocuments = new WeakSet();
|
|
668
|
+
/**
|
|
669
|
+
* Which editor last painted into the shared registry. Only that instance may clear it, so a
|
|
670
|
+
* second surface on the page cannot wipe the first one's find painting when its own find bar
|
|
671
|
+
* closes. Two live searches still share one painting — the newer one wins, which is what a
|
|
672
|
+
* single visible "current match" should mean.
|
|
673
|
+
*/
|
|
674
|
+
let findPaintOwner = null;
|
|
675
|
+
function ensureFindHighlightStyles(doc) {
|
|
676
|
+
if (findHighlightStyledDocuments.has(doc))
|
|
677
|
+
return;
|
|
678
|
+
findHighlightStyledDocuments.add(doc);
|
|
679
|
+
const style = doc.createElement("style");
|
|
680
|
+
style.dataset.docxodusFindHighlight = "true";
|
|
681
|
+
style.textContent = `
|
|
682
|
+
::highlight(${FIND_HIGHLIGHT}) { background-color: rgba(15, 118, 110, .16); }
|
|
683
|
+
::highlight(${FIND_HIGHLIGHT_ACTIVE}) { background-color: rgba(15, 118, 110, .38); }
|
|
684
|
+
`;
|
|
685
|
+
(doc.head ?? doc.documentElement).appendChild(style);
|
|
686
|
+
}
|
|
687
|
+
function highlightRegistry() {
|
|
688
|
+
if (typeof CSS === "undefined")
|
|
689
|
+
return null;
|
|
690
|
+
const registry = CSS.highlights;
|
|
691
|
+
const ctor = globalThis.Highlight;
|
|
692
|
+
return registry && typeof ctor === "function" ? registry : null;
|
|
693
|
+
}
|
|
694
|
+
function makeHighlight(ranges) {
|
|
695
|
+
const ctor = globalThis.Highlight;
|
|
696
|
+
return new ctor(...ranges);
|
|
697
|
+
}
|
|
639
698
|
/**
|
|
640
699
|
* True when `el`'s immediate parent is a paragraph-border `<div>` the full render wrapped it in
|
|
641
700
|
* (CreateBorderDivs groups visibly-bordered paragraphs into a div). The body wrapper div has no
|
|
@@ -1021,6 +1080,7 @@ export class DocxEditor {
|
|
|
1021
1080
|
document.removeEventListener("mouseup", this.onMouseUp, true);
|
|
1022
1081
|
}
|
|
1023
1082
|
this.clearDragSelection();
|
|
1083
|
+
this.clearFindMatches();
|
|
1024
1084
|
this.teardownBlockDrag();
|
|
1025
1085
|
this.gutter?.dispose();
|
|
1026
1086
|
this.gutter = null;
|
|
@@ -3711,16 +3771,75 @@ export class DocxEditor {
|
|
|
3711
3771
|
}
|
|
3712
3772
|
return out;
|
|
3713
3773
|
}
|
|
3714
|
-
/**
|
|
3774
|
+
/**
|
|
3775
|
+
* Select a match, focus its block and scroll it into view — the caret lands on the hit, so
|
|
3776
|
+
* typing continues in the document. A find box that is still being typed into wants
|
|
3777
|
+
* `showFindMatches` instead; this is the commit step (closing the bar, jumping in to edit).
|
|
3778
|
+
*/
|
|
3715
3779
|
selectMatch(match) {
|
|
3716
3780
|
if (!match.block.isConnected)
|
|
3717
3781
|
return;
|
|
3782
|
+
this.clearFindMatches();
|
|
3718
3783
|
this.activeBlock = match.block;
|
|
3719
3784
|
selectRange(match.block, match.start, match.length);
|
|
3720
3785
|
match.block.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
3721
3786
|
}
|
|
3722
|
-
/**
|
|
3723
|
-
|
|
3787
|
+
/**
|
|
3788
|
+
* Paint `matches` and scroll the one at `activeIndex` into view WITHOUT moving focus or the
|
|
3789
|
+
* caret. This is what a find field calls on every keystroke: the document shows where the hits
|
|
3790
|
+
* are and rides to the current one, while the keyboard stays in the search box.
|
|
3791
|
+
*
|
|
3792
|
+
* Falls back to the document selection where the CSS Custom Highlight API is missing — still
|
|
3793
|
+
* without focusing the block, so the next character typed goes to the search field either way.
|
|
3794
|
+
*/
|
|
3795
|
+
showFindMatches(matches, activeIndex) {
|
|
3796
|
+
const active = matches[activeIndex];
|
|
3797
|
+
this.clearFindMatches();
|
|
3798
|
+
if (!active || !active.block.isConnected)
|
|
3799
|
+
return;
|
|
3800
|
+
this.activeBlock = active.block;
|
|
3801
|
+
const activeRange = contentRangeIn(active.block, active.start, active.length);
|
|
3802
|
+
const registry = highlightRegistry();
|
|
3803
|
+
if (registry && activeRange) {
|
|
3804
|
+
ensureFindHighlightStyles(this.container.ownerDocument);
|
|
3805
|
+
const rest = [];
|
|
3806
|
+
for (let i = 0; i < matches.length && rest.length < FIND_PAINT_LIMIT; i++) {
|
|
3807
|
+
if (i === activeIndex)
|
|
3808
|
+
continue;
|
|
3809
|
+
const range = contentRangeIn(matches[i].block, matches[i].start, matches[i].length);
|
|
3810
|
+
if (range)
|
|
3811
|
+
rest.push(range);
|
|
3812
|
+
}
|
|
3813
|
+
if (rest.length > 0)
|
|
3814
|
+
registry.set(FIND_HIGHLIGHT, makeHighlight(rest));
|
|
3815
|
+
registry.set(FIND_HIGHLIGHT_ACTIVE, makeHighlight([activeRange]));
|
|
3816
|
+
findPaintOwner = this;
|
|
3817
|
+
}
|
|
3818
|
+
else if (activeRange) {
|
|
3819
|
+
// No highlight registry (older Firefox/Safari): the document selection is the only paint
|
|
3820
|
+
// available. It is not registry-owned, so `clearFindMatches` leaves it — the next
|
|
3821
|
+
// `showFindMatches` or the closing `selectMatch` overwrites it.
|
|
3822
|
+
const sel = this.container.ownerDocument.defaultView?.getSelection();
|
|
3823
|
+
sel?.removeAllRanges();
|
|
3824
|
+
sel?.addRange(activeRange);
|
|
3825
|
+
}
|
|
3826
|
+
active.block.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
3827
|
+
}
|
|
3828
|
+
/** Drop the find painting (closing the find bar, or committing a match to the caret). */
|
|
3829
|
+
clearFindMatches() {
|
|
3830
|
+
if (findPaintOwner !== this)
|
|
3831
|
+
return;
|
|
3832
|
+
const registry = highlightRegistry();
|
|
3833
|
+
registry?.delete(FIND_HIGHLIGHT);
|
|
3834
|
+
registry?.delete(FIND_HIGHLIGHT_ACTIVE);
|
|
3835
|
+
findPaintOwner = null;
|
|
3836
|
+
}
|
|
3837
|
+
/**
|
|
3838
|
+
* Replace one match's text (formatting of the surrounding run is inherited). `focus: false`
|
|
3839
|
+
* leaves the keyboard where it is — a Replace button pressed from the find bar must not drag
|
|
3840
|
+
* the caret into the document mid-search.
|
|
3841
|
+
*/
|
|
3842
|
+
replaceMatch(match, replacement, options = {}) {
|
|
3724
3843
|
const block = match.block;
|
|
3725
3844
|
if (this.closed || !block.isConnected)
|
|
3726
3845
|
return false;
|
|
@@ -3738,7 +3857,7 @@ export class DocxEditor {
|
|
|
3738
3857
|
if (!res.success)
|
|
3739
3858
|
return false;
|
|
3740
3859
|
const fresh = this.swapBlock(block, unid, res.modified?.[0]);
|
|
3741
|
-
if (fresh)
|
|
3860
|
+
if (fresh && options.focus !== false)
|
|
3742
3861
|
selectRange(fresh, span.start, replacement.length);
|
|
3743
3862
|
return true;
|
|
3744
3863
|
}
|
|
@@ -3761,7 +3880,7 @@ export class DocxEditor {
|
|
|
3761
3880
|
if (!current.isConnected)
|
|
3762
3881
|
break;
|
|
3763
3882
|
const before = current;
|
|
3764
|
-
if (this.replaceMatch({ block: current, start: m.start, length: m.length }, replacement)) {
|
|
3883
|
+
if (this.replaceMatch({ block: current, start: m.start, length: m.length }, replacement, { focus: options.focus })) {
|
|
3765
3884
|
count++;
|
|
3766
3885
|
// swapBlock replaced the node; keep following it.
|
|
3767
3886
|
current = this.activeBlock && this.activeBlock !== before ? this.activeBlock : current;
|