@harbour-enterprises/superdoc 1.6.0-next.1 → 1.6.0-next.2
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/chunks/{PdfViewer-Dr4K1JKD.cjs → PdfViewer-D_YseVgi.cjs} +2 -2
- package/dist/chunks/{PdfViewer-DzppMlfu.es.js → PdfViewer-Djd4zd7C.es.js} +2 -2
- package/dist/chunks/{SuperConverter-Cyn9peRO.es.js → SuperConverter-BAUfsE-s.es.js} +1 -1
- package/dist/chunks/{SuperConverter-DpKjVrPl.cjs → SuperConverter-DrhNM5Cd.cjs} +1 -1
- package/dist/chunks/{index-CmZ15rIJ.cjs → index-CyiEB9wO.cjs} +4 -4
- package/dist/chunks/{index-Cto-XsBE.cjs → index-D-7HfDnq.cjs} +636 -54
- package/dist/chunks/{index-DrcLOCfC.es.js → index-ajHpG3f-.es.js} +4 -4
- package/dist/chunks/{index-C7KECpDt.es.js → index-fbohTSaQ.es.js} +636 -54
- package/dist/super-editor/converter.cjs +1 -1
- package/dist/super-editor/converter.es.js +1 -1
- package/dist/super-editor.cjs +2 -2
- package/dist/super-editor.es.js +3 -3
- package/dist/superdoc/src/dev/components/sidebar/SidebarSearch.vue.d.ts +5 -0
- package/dist/superdoc/src/dev/components/sidebar/SidebarSearch.vue.d.ts.map +1 -0
- package/dist/superdoc.cjs +3 -3
- package/dist/superdoc.es.js +3 -3
- package/dist/superdoc.umd.js +637 -55
- package/dist/superdoc.umd.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { B as Buffer$2 } from "./jszip-B1fkPkPJ.es.js";
|
|
2
2
|
import { t as twipsToInches, i as inchesToTwips, p as ptToTwips, l as linesToTwips, a as twipsToLines, b as pixelsToTwips, h as halfPointToPoints, c as twipsToPixels$2, d as convertSizeToCSS, e as inchesToPixels } from "./helpers-C8e9wR5l.es.js";
|
|
3
|
-
import { g as generateDocxRandomId, T as TextSelection$1, o as objectIncludes, w as wrapTextsInRuns, D as DOMParser$1, c as createDocFromMarkdown, a as createDocFromHTML, b as chainableEditorState, d as convertMarkdownToHTML, f as findParentNode, e as findParentNodeClosestToPos, h as generateRandom32BitHex, i as generateRandomSigned32BitIntStrId, P as PluginKey, j as Plugin, M as Mapping, N as NodeSelection, k as Selection, l as Slice, m as DOMSerializer, F as Fragment, n as Mark$1, p as dropPoint, A as AllSelection, q as Schema$1, s as canSplit, t as resolveRunProperties, u as encodeMarksFromRPr, v as liftTarget, x as canJoin, y as joinPoint, z as replaceStep$1, R as ReplaceAroundStep$1, B as htmlHandler, C as ReplaceStep, E as getResolvedParagraphProperties, G as changeListLevel, H as isList$1, I as updateNumberingProperties, L as ListHelpers, J as inputRulesPlugin, K as TrackDeleteMarkName$1, O as TrackInsertMarkName$1, Q as TrackFormatMarkName$1, U as AddMarkStep, V as RemoveMarkStep, W as CommandService, S as SuperConverter, X as EditorState, Y as unflattenListsInHtml, Z as SelectionRange, _ as Transform, $ as createOoxmlResolver, a0 as translator, a1 as translator$1, a2 as resolveDocxFontFamily, a3 as combineIndentProperties, a4 as _getReferencedTableStyles, a5 as decodeRPrFromMarks, a6 as calculateResolvedParagraphProperties, a7 as encodeCSSFromPPr, a8 as encodeCSSFromRPr, a9 as generateOrderedListIndex, aa as docxNumberingHelpers, ab as InputRule, ac as insertNewRelationship, ad as kebabCase$1, ae as getUnderlineCssString } from "./SuperConverter-
|
|
3
|
+
import { g as generateDocxRandomId, T as TextSelection$1, o as objectIncludes, w as wrapTextsInRuns, D as DOMParser$1, c as createDocFromMarkdown, a as createDocFromHTML, b as chainableEditorState, d as convertMarkdownToHTML, f as findParentNode, e as findParentNodeClosestToPos, h as generateRandom32BitHex, i as generateRandomSigned32BitIntStrId, P as PluginKey, j as Plugin, M as Mapping, N as NodeSelection, k as Selection, l as Slice, m as DOMSerializer, F as Fragment, n as Mark$1, p as dropPoint, A as AllSelection, q as Schema$1, s as canSplit, t as resolveRunProperties, u as encodeMarksFromRPr, v as liftTarget, x as canJoin, y as joinPoint, z as replaceStep$1, R as ReplaceAroundStep$1, B as htmlHandler, C as ReplaceStep, E as getResolvedParagraphProperties, G as changeListLevel, H as isList$1, I as updateNumberingProperties, L as ListHelpers, J as inputRulesPlugin, K as TrackDeleteMarkName$1, O as TrackInsertMarkName$1, Q as TrackFormatMarkName$1, U as AddMarkStep, V as RemoveMarkStep, W as CommandService, S as SuperConverter, X as EditorState, Y as unflattenListsInHtml, Z as SelectionRange, _ as Transform, $ as createOoxmlResolver, a0 as translator, a1 as translator$1, a2 as resolveDocxFontFamily, a3 as combineIndentProperties, a4 as _getReferencedTableStyles, a5 as decodeRPrFromMarks, a6 as calculateResolvedParagraphProperties, a7 as encodeCSSFromPPr, a8 as encodeCSSFromRPr, a9 as generateOrderedListIndex, aa as docxNumberingHelpers, ab as InputRule, ac as insertNewRelationship, ad as kebabCase$1, ae as getUnderlineCssString } from "./SuperConverter-BAUfsE-s.es.js";
|
|
4
4
|
import { p as process$1, r as ref, C as global$1, c as computed, E as createElementBlock, F as Fragment$1, S as renderList, O as withModifiers, G as openBlock, P as normalizeClass, M as createCommentVNode, H as toDisplayString, K as createBaseVNode, U as createApp, f as onMounted, X as onUnmounted, R as withDirectives, v as unref, Y as vModelText, y as nextTick, L as normalizeStyle, u as watch, Z as withKeys, _ as createTextVNode, I as createVNode, h as h$1, $ as readonly, s as getCurrentInstance, o as onBeforeUnmount, j as reactive, b as onBeforeMount, i as inject, a0 as onActivated, a1 as onDeactivated, a2 as Comment, d as defineComponent, a as provide, g as Teleport, t as toRef, a3 as renderSlot, a4 as isVNode, D as shallowRef, w as watchEffect, T as Transition, a5 as mergeProps, a6 as vShow, a7 as cloneVNode, a8 as Text$2, m as markRaw, N as createBlock, J as withCtx, a9 as useCssVars, V as resolveDynamicComponent, aa as normalizeProps, ab as guardReactiveProps } from "./vue-BnBKJwCW.es.js";
|
|
5
5
|
import "./jszip.min-DCl8qkFO.es.js";
|
|
6
6
|
import { E as EventEmitter$1 } from "./eventemitter3-CwrdEv8r.es.js";
|
|
@@ -9370,6 +9370,158 @@ class Schema {
|
|
|
9370
9370
|
return Object.fromEntries(markEntries);
|
|
9371
9371
|
}
|
|
9372
9372
|
}
|
|
9373
|
+
const positionTrackerKey = new PluginKey("positionTracker");
|
|
9374
|
+
function createPositionTrackerPlugin() {
|
|
9375
|
+
return new Plugin({
|
|
9376
|
+
key: positionTrackerKey,
|
|
9377
|
+
state: {
|
|
9378
|
+
init() {
|
|
9379
|
+
return {
|
|
9380
|
+
decorations: DecorationSet.empty,
|
|
9381
|
+
generation: 0
|
|
9382
|
+
};
|
|
9383
|
+
},
|
|
9384
|
+
apply(tr, state) {
|
|
9385
|
+
let { decorations, generation } = state;
|
|
9386
|
+
const meta = tr.getMeta(positionTrackerKey);
|
|
9387
|
+
if (meta?.action === "add") {
|
|
9388
|
+
decorations = decorations.add(tr.doc, meta.decorations);
|
|
9389
|
+
} else if (meta?.action === "remove") {
|
|
9390
|
+
const toRemove = decorations.find().filter((decoration) => meta.ids.includes(decoration.spec.id));
|
|
9391
|
+
decorations = decorations.remove(toRemove);
|
|
9392
|
+
} else if (meta?.action === "removeByType") {
|
|
9393
|
+
const toRemove = decorations.find().filter((decoration) => decoration.spec.type === meta.type);
|
|
9394
|
+
decorations = decorations.remove(toRemove);
|
|
9395
|
+
}
|
|
9396
|
+
if (tr.docChanged) {
|
|
9397
|
+
decorations = decorations.map(tr.mapping, tr.doc);
|
|
9398
|
+
generation += 1;
|
|
9399
|
+
}
|
|
9400
|
+
return { decorations, generation };
|
|
9401
|
+
}
|
|
9402
|
+
},
|
|
9403
|
+
props: {
|
|
9404
|
+
decorations() {
|
|
9405
|
+
return DecorationSet.empty;
|
|
9406
|
+
}
|
|
9407
|
+
}
|
|
9408
|
+
});
|
|
9409
|
+
}
|
|
9410
|
+
class PositionTracker {
|
|
9411
|
+
#editor;
|
|
9412
|
+
constructor(editor) {
|
|
9413
|
+
this.#editor = editor;
|
|
9414
|
+
}
|
|
9415
|
+
#getState() {
|
|
9416
|
+
if (!this.#editor?.state) return null;
|
|
9417
|
+
return positionTrackerKey.getState(this.#editor.state) ?? null;
|
|
9418
|
+
}
|
|
9419
|
+
track(from3, to, spec) {
|
|
9420
|
+
const id = v4();
|
|
9421
|
+
if (!this.#editor?.state) return id;
|
|
9422
|
+
const fullSpec = { kind: "range", ...spec, id };
|
|
9423
|
+
const deco = Decoration.inline(from3, to, {}, fullSpec);
|
|
9424
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9425
|
+
action: "add",
|
|
9426
|
+
decorations: [deco]
|
|
9427
|
+
}).setMeta("addToHistory", false);
|
|
9428
|
+
this.#editor.dispatch(tr);
|
|
9429
|
+
return id;
|
|
9430
|
+
}
|
|
9431
|
+
trackMany(ranges) {
|
|
9432
|
+
if (!this.#editor?.state) {
|
|
9433
|
+
return ranges.map(() => v4());
|
|
9434
|
+
}
|
|
9435
|
+
const ids = [];
|
|
9436
|
+
const decorations = [];
|
|
9437
|
+
for (const { from: from3, to, spec } of ranges) {
|
|
9438
|
+
const id = v4();
|
|
9439
|
+
ids.push(id);
|
|
9440
|
+
const fullSpec = { kind: "range", ...spec, id };
|
|
9441
|
+
decorations.push(Decoration.inline(from3, to, {}, fullSpec));
|
|
9442
|
+
}
|
|
9443
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9444
|
+
action: "add",
|
|
9445
|
+
decorations
|
|
9446
|
+
}).setMeta("addToHistory", false);
|
|
9447
|
+
this.#editor.dispatch(tr);
|
|
9448
|
+
return ids;
|
|
9449
|
+
}
|
|
9450
|
+
untrack(id) {
|
|
9451
|
+
if (!this.#editor?.state) return;
|
|
9452
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9453
|
+
action: "remove",
|
|
9454
|
+
ids: [id]
|
|
9455
|
+
}).setMeta("addToHistory", false);
|
|
9456
|
+
this.#editor.dispatch(tr);
|
|
9457
|
+
}
|
|
9458
|
+
untrackMany(ids) {
|
|
9459
|
+
if (!this.#editor?.state || ids.length === 0) return;
|
|
9460
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9461
|
+
action: "remove",
|
|
9462
|
+
ids
|
|
9463
|
+
}).setMeta("addToHistory", false);
|
|
9464
|
+
this.#editor.dispatch(tr);
|
|
9465
|
+
}
|
|
9466
|
+
untrackByType(type) {
|
|
9467
|
+
if (!this.#editor?.state) return;
|
|
9468
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9469
|
+
action: "removeByType",
|
|
9470
|
+
type
|
|
9471
|
+
}).setMeta("addToHistory", false);
|
|
9472
|
+
this.#editor.dispatch(tr);
|
|
9473
|
+
}
|
|
9474
|
+
resolve(id) {
|
|
9475
|
+
const state = this.#getState();
|
|
9476
|
+
if (!state) return null;
|
|
9477
|
+
const found = state.decorations.find().find((decoration) => decoration.spec.id === id);
|
|
9478
|
+
if (!found) return null;
|
|
9479
|
+
const spec = found.spec;
|
|
9480
|
+
return {
|
|
9481
|
+
id: spec.id,
|
|
9482
|
+
from: found.from,
|
|
9483
|
+
to: found.to,
|
|
9484
|
+
spec
|
|
9485
|
+
};
|
|
9486
|
+
}
|
|
9487
|
+
resolveMany(ids) {
|
|
9488
|
+
const result = /* @__PURE__ */ new Map();
|
|
9489
|
+
for (const id of ids) {
|
|
9490
|
+
result.set(id, null);
|
|
9491
|
+
}
|
|
9492
|
+
const state = this.#getState();
|
|
9493
|
+
if (!state || ids.length === 0) return result;
|
|
9494
|
+
const idSet = new Set(ids);
|
|
9495
|
+
for (const decoration of state.decorations.find()) {
|
|
9496
|
+
const spec = decoration.spec;
|
|
9497
|
+
if (idSet.has(spec.id)) {
|
|
9498
|
+
result.set(spec.id, {
|
|
9499
|
+
id: spec.id,
|
|
9500
|
+
from: decoration.from,
|
|
9501
|
+
to: decoration.to,
|
|
9502
|
+
spec
|
|
9503
|
+
});
|
|
9504
|
+
}
|
|
9505
|
+
}
|
|
9506
|
+
return result;
|
|
9507
|
+
}
|
|
9508
|
+
findByType(type) {
|
|
9509
|
+
const state = this.#getState();
|
|
9510
|
+
if (!state) return [];
|
|
9511
|
+
return state.decorations.find().filter((decoration) => decoration.spec.type === type).map((decoration) => {
|
|
9512
|
+
const spec = decoration.spec;
|
|
9513
|
+
return {
|
|
9514
|
+
id: spec.id,
|
|
9515
|
+
from: decoration.from,
|
|
9516
|
+
to: decoration.to,
|
|
9517
|
+
spec
|
|
9518
|
+
};
|
|
9519
|
+
});
|
|
9520
|
+
}
|
|
9521
|
+
get generation() {
|
|
9522
|
+
return this.#getState()?.generation ?? 0;
|
|
9523
|
+
}
|
|
9524
|
+
}
|
|
9373
9525
|
class OxmlNode extends Node$1 {
|
|
9374
9526
|
constructor(config) {
|
|
9375
9527
|
super(config);
|
|
@@ -11730,6 +11882,34 @@ const EditorFocus = Extension.create({
|
|
|
11730
11882
|
return [editorFocusPlugin];
|
|
11731
11883
|
}
|
|
11732
11884
|
});
|
|
11885
|
+
const PositionTrackerExtension = Extension.create({
|
|
11886
|
+
name: "positionTracker",
|
|
11887
|
+
addStorage() {
|
|
11888
|
+
return {
|
|
11889
|
+
tracker: null
|
|
11890
|
+
};
|
|
11891
|
+
},
|
|
11892
|
+
addPmPlugins() {
|
|
11893
|
+
return [createPositionTrackerPlugin()];
|
|
11894
|
+
},
|
|
11895
|
+
onCreate() {
|
|
11896
|
+
const existing = this.editor?.positionTracker ?? this.storage.tracker;
|
|
11897
|
+
if (existing) {
|
|
11898
|
+
this.storage.tracker = existing;
|
|
11899
|
+
this.editor.positionTracker = existing;
|
|
11900
|
+
return;
|
|
11901
|
+
}
|
|
11902
|
+
const tracker = new PositionTracker(this.editor);
|
|
11903
|
+
this.storage.tracker = tracker;
|
|
11904
|
+
this.editor.positionTracker = tracker;
|
|
11905
|
+
},
|
|
11906
|
+
onDestroy() {
|
|
11907
|
+
if (this.editor?.positionTracker === this.storage.tracker) {
|
|
11908
|
+
this.editor.positionTracker = null;
|
|
11909
|
+
}
|
|
11910
|
+
this.storage.tracker = null;
|
|
11911
|
+
}
|
|
11912
|
+
});
|
|
11733
11913
|
class EventEmitter {
|
|
11734
11914
|
#events = /* @__PURE__ */ new Map();
|
|
11735
11915
|
/**
|
|
@@ -15507,7 +15687,7 @@ const canUseDOM = () => {
|
|
|
15507
15687
|
return false;
|
|
15508
15688
|
}
|
|
15509
15689
|
};
|
|
15510
|
-
const summaryVersion = "1.6.0-next.
|
|
15690
|
+
const summaryVersion = "1.6.0-next.2";
|
|
15511
15691
|
const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
|
|
15512
15692
|
const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
|
|
15513
15693
|
function mapAttributes(attrs) {
|
|
@@ -17000,7 +17180,7 @@ class Editor extends EventEmitter {
|
|
|
17000
17180
|
*/
|
|
17001
17181
|
#createExtensionService() {
|
|
17002
17182
|
const allowedExtensions = ["extension", "node", "mark"];
|
|
17003
|
-
const coreExtensions = [Editable, Commands, EditorFocus, Keymap];
|
|
17183
|
+
const coreExtensions = [Editable, Commands, EditorFocus, Keymap, PositionTrackerExtension];
|
|
17004
17184
|
const externalExtensions = this.options.externalExtensions || [];
|
|
17005
17185
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter((extension) => {
|
|
17006
17186
|
const extensionType = typeof extension?.type === "string" ? extension.type : void 0;
|
|
@@ -18174,7 +18354,7 @@ class Editor extends EventEmitter {
|
|
|
18174
18354
|
* Process collaboration migrations
|
|
18175
18355
|
*/
|
|
18176
18356
|
processCollaborationMigrations() {
|
|
18177
|
-
console.debug("[checkVersionMigrations] Current editor version", "1.6.0-next.
|
|
18357
|
+
console.debug("[checkVersionMigrations] Current editor version", "1.6.0-next.2");
|
|
18178
18358
|
if (!this.options.ydoc) return;
|
|
18179
18359
|
const metaMap = this.options.ydoc.getMap("meta");
|
|
18180
18360
|
let docVersion = metaMap.get("version");
|
|
@@ -75527,14 +75707,317 @@ function getMatchHighlights(state) {
|
|
|
75527
75707
|
let search2 = searchKey.getState(state);
|
|
75528
75708
|
return search2 ? search2.deco : DecorationSet.empty;
|
|
75529
75709
|
}
|
|
75530
|
-
|
|
75531
|
-
|
|
75532
|
-
|
|
75710
|
+
const BLOCK_SEPARATOR = "\n";
|
|
75711
|
+
const ATOM_PLACEHOLDER = "";
|
|
75712
|
+
class SearchIndex {
|
|
75713
|
+
/** @type {string} */
|
|
75714
|
+
text = "";
|
|
75715
|
+
/** @type {Segment[]} */
|
|
75716
|
+
segments = [];
|
|
75717
|
+
/** @type {boolean} */
|
|
75718
|
+
valid = false;
|
|
75719
|
+
/** @type {number} */
|
|
75720
|
+
docSize = 0;
|
|
75721
|
+
/**
|
|
75722
|
+
* Build the search index from a ProseMirror document.
|
|
75723
|
+
* Uses doc.textBetween for the flattened string and walks
|
|
75724
|
+
* the document to build the segment offset map.
|
|
75725
|
+
*
|
|
75726
|
+
* @param {import('prosemirror-model').Node} doc - The ProseMirror document
|
|
75727
|
+
*/
|
|
75728
|
+
build(doc2) {
|
|
75729
|
+
this.text = doc2.textBetween(0, doc2.content.size, BLOCK_SEPARATOR, ATOM_PLACEHOLDER);
|
|
75730
|
+
this.segments = [];
|
|
75731
|
+
this.docSize = doc2.content.size;
|
|
75732
|
+
let offset2 = 0;
|
|
75733
|
+
this.#walkNodeContent(doc2, 0, offset2, (segment) => {
|
|
75734
|
+
this.segments.push(segment);
|
|
75735
|
+
offset2 = segment.offsetEnd;
|
|
75736
|
+
});
|
|
75737
|
+
this.valid = true;
|
|
75738
|
+
}
|
|
75739
|
+
/**
|
|
75740
|
+
* Walk the content of a node to build segments.
|
|
75741
|
+
* This method processes the children of a node, given the position
|
|
75742
|
+
* where the node's content starts.
|
|
75743
|
+
*
|
|
75744
|
+
* @param {import('prosemirror-model').Node} node - Current node
|
|
75745
|
+
* @param {number} contentStart - Document position where this node's content starts
|
|
75746
|
+
* @param {number} offset - Current offset in flattened string
|
|
75747
|
+
* @param {(segment: Segment) => void} addSegment - Callback to add a segment
|
|
75748
|
+
* @returns {number} The new offset after processing this node's content
|
|
75749
|
+
*/
|
|
75750
|
+
#walkNodeContent(node, contentStart, offset2, addSegment) {
|
|
75751
|
+
let currentOffset = offset2;
|
|
75752
|
+
let isFirstChild = true;
|
|
75753
|
+
node.forEach((child, childContentOffset) => {
|
|
75754
|
+
const childDocPos = contentStart + childContentOffset;
|
|
75755
|
+
if (child.isBlock && !isFirstChild) {
|
|
75756
|
+
addSegment({
|
|
75757
|
+
offsetStart: currentOffset,
|
|
75758
|
+
offsetEnd: currentOffset + 1,
|
|
75759
|
+
docFrom: childDocPos,
|
|
75760
|
+
docTo: childDocPos,
|
|
75761
|
+
kind: "blockSep"
|
|
75762
|
+
});
|
|
75763
|
+
currentOffset += 1;
|
|
75764
|
+
}
|
|
75765
|
+
currentOffset = this.#walkNode(child, childDocPos, currentOffset, addSegment);
|
|
75766
|
+
isFirstChild = false;
|
|
75767
|
+
});
|
|
75768
|
+
return currentOffset;
|
|
75769
|
+
}
|
|
75770
|
+
/**
|
|
75771
|
+
* Recursively walk a node and its descendants to build segments.
|
|
75772
|
+
*
|
|
75773
|
+
* @param {import('prosemirror-model').Node} node - Current node
|
|
75774
|
+
* @param {number} docPos - Document position at start of this node
|
|
75775
|
+
* @param {number} offset - Current offset in flattened string
|
|
75776
|
+
* @param {(segment: Segment) => void} addSegment - Callback to add a segment
|
|
75777
|
+
* @returns {number} The new offset after processing this node
|
|
75778
|
+
*/
|
|
75779
|
+
#walkNode(node, docPos, offset2, addSegment) {
|
|
75780
|
+
if (node.isText) {
|
|
75781
|
+
const text = node.text || "";
|
|
75782
|
+
if (text.length > 0) {
|
|
75783
|
+
addSegment({
|
|
75784
|
+
offsetStart: offset2,
|
|
75785
|
+
offsetEnd: offset2 + text.length,
|
|
75786
|
+
docFrom: docPos,
|
|
75787
|
+
docTo: docPos + text.length,
|
|
75788
|
+
kind: "text"
|
|
75789
|
+
});
|
|
75790
|
+
return offset2 + text.length;
|
|
75791
|
+
}
|
|
75792
|
+
return offset2;
|
|
75793
|
+
}
|
|
75794
|
+
if (node.isLeaf) {
|
|
75795
|
+
if (node.type.name === "hard_break") {
|
|
75796
|
+
addSegment({
|
|
75797
|
+
offsetStart: offset2,
|
|
75798
|
+
offsetEnd: offset2 + 1,
|
|
75799
|
+
docFrom: docPos,
|
|
75800
|
+
docTo: docPos + node.nodeSize,
|
|
75801
|
+
kind: "hardBreak"
|
|
75802
|
+
});
|
|
75803
|
+
return offset2 + 1;
|
|
75804
|
+
}
|
|
75805
|
+
addSegment({
|
|
75806
|
+
offsetStart: offset2,
|
|
75807
|
+
offsetEnd: offset2 + 1,
|
|
75808
|
+
docFrom: docPos,
|
|
75809
|
+
docTo: docPos + node.nodeSize,
|
|
75810
|
+
kind: "atom"
|
|
75811
|
+
});
|
|
75812
|
+
return offset2 + 1;
|
|
75813
|
+
}
|
|
75814
|
+
return this.#walkNodeContent(node, docPos + 1, offset2, addSegment);
|
|
75815
|
+
}
|
|
75816
|
+
/**
|
|
75817
|
+
* Mark the index as stale. It will be rebuilt on next search.
|
|
75818
|
+
*/
|
|
75819
|
+
invalidate() {
|
|
75820
|
+
this.valid = false;
|
|
75821
|
+
}
|
|
75822
|
+
/**
|
|
75823
|
+
* Check if the index needs rebuilding for the given document.
|
|
75824
|
+
*
|
|
75825
|
+
* @param {import('prosemirror-model').Node} doc - The document to check against
|
|
75826
|
+
* @returns {boolean} True if index is stale and needs rebuilding
|
|
75827
|
+
*/
|
|
75828
|
+
isStale(doc2) {
|
|
75829
|
+
return !this.valid || doc2.content.size !== this.docSize;
|
|
75830
|
+
}
|
|
75831
|
+
/**
|
|
75832
|
+
* Ensure the index is valid for the given document.
|
|
75833
|
+
* Rebuilds if stale.
|
|
75834
|
+
*
|
|
75835
|
+
* @param {import('prosemirror-model').Node} doc - The document
|
|
75836
|
+
*/
|
|
75837
|
+
ensureValid(doc2) {
|
|
75838
|
+
if (this.isStale(doc2)) {
|
|
75839
|
+
this.build(doc2);
|
|
75840
|
+
}
|
|
75841
|
+
}
|
|
75842
|
+
/**
|
|
75843
|
+
* Convert an offset range in the flattened string to document ranges.
|
|
75844
|
+
* Skips separator/atom segments and returns only text ranges.
|
|
75845
|
+
*
|
|
75846
|
+
* @param {number} start - Start offset in flattened string
|
|
75847
|
+
* @param {number} end - End offset in flattened string
|
|
75848
|
+
* @returns {DocRange[]} Array of document ranges (text segments only)
|
|
75849
|
+
*/
|
|
75850
|
+
offsetRangeToDocRanges(start2, end2) {
|
|
75851
|
+
const ranges = [];
|
|
75852
|
+
for (const segment of this.segments) {
|
|
75853
|
+
if (segment.offsetEnd <= start2) continue;
|
|
75854
|
+
if (segment.offsetStart >= end2) break;
|
|
75855
|
+
if (segment.kind !== "text") continue;
|
|
75856
|
+
const overlapStart = Math.max(start2, segment.offsetStart);
|
|
75857
|
+
const overlapEnd = Math.min(end2, segment.offsetEnd);
|
|
75858
|
+
if (overlapStart < overlapEnd) {
|
|
75859
|
+
const startInSegment = overlapStart - segment.offsetStart;
|
|
75860
|
+
const endInSegment = overlapEnd - segment.offsetStart;
|
|
75861
|
+
ranges.push({
|
|
75862
|
+
from: segment.docFrom + startInSegment,
|
|
75863
|
+
to: segment.docFrom + endInSegment
|
|
75864
|
+
});
|
|
75865
|
+
}
|
|
75866
|
+
}
|
|
75867
|
+
return ranges;
|
|
75868
|
+
}
|
|
75869
|
+
/**
|
|
75870
|
+
* Find the document position for a given offset in the flattened string.
|
|
75871
|
+
*
|
|
75872
|
+
* @param {number} offset - Offset in flattened string
|
|
75873
|
+
* @returns {number|null} Document position, or null if not found
|
|
75874
|
+
*/
|
|
75875
|
+
offsetToDocPos(offset2) {
|
|
75876
|
+
for (const segment of this.segments) {
|
|
75877
|
+
if (offset2 >= segment.offsetStart && offset2 < segment.offsetEnd) {
|
|
75878
|
+
if (segment.kind === "text") {
|
|
75879
|
+
return segment.docFrom + (offset2 - segment.offsetStart);
|
|
75880
|
+
}
|
|
75881
|
+
return segment.docFrom;
|
|
75882
|
+
}
|
|
75883
|
+
}
|
|
75884
|
+
if (this.segments.length > 0 && offset2 === this.segments[this.segments.length - 1].offsetEnd) {
|
|
75885
|
+
const lastSeg = this.segments[this.segments.length - 1];
|
|
75886
|
+
return lastSeg.docTo;
|
|
75887
|
+
}
|
|
75888
|
+
return null;
|
|
75889
|
+
}
|
|
75890
|
+
/**
|
|
75891
|
+
* Escape special regex characters in a string.
|
|
75892
|
+
*
|
|
75893
|
+
* @param {string} str - String to escape
|
|
75894
|
+
* @returns {string} Escaped string safe for use in RegExp
|
|
75895
|
+
*/
|
|
75896
|
+
static escapeRegex(str) {
|
|
75897
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
75898
|
+
}
|
|
75899
|
+
/**
|
|
75900
|
+
* Convert a plain search string to a whitespace-flexible regex pattern.
|
|
75901
|
+
* This allows matching across paragraph boundaries.
|
|
75902
|
+
*
|
|
75903
|
+
* @param {string} searchString - The search string
|
|
75904
|
+
* @returns {string} Regex pattern string
|
|
75905
|
+
*/
|
|
75906
|
+
static toFlexiblePattern(searchString) {
|
|
75907
|
+
const parts = searchString.split(/\s+/).filter((part) => part.length > 0);
|
|
75908
|
+
if (parts.length === 0) return "";
|
|
75909
|
+
return parts.map((part) => SearchIndex.escapeRegex(part)).join("\\s+");
|
|
75910
|
+
}
|
|
75911
|
+
/**
|
|
75912
|
+
* Search the index for matches.
|
|
75913
|
+
*
|
|
75914
|
+
* @param {string | RegExp} pattern - Search pattern (string or regex)
|
|
75915
|
+
* @param {Object} options - Search options
|
|
75916
|
+
* @param {boolean} [options.caseSensitive=false] - Case sensitive search
|
|
75917
|
+
* @param {number} [options.maxMatches=1000] - Maximum number of matches to return
|
|
75918
|
+
* @returns {Array<{start: number, end: number, text: string}>} Array of matches with offsets
|
|
75919
|
+
*/
|
|
75920
|
+
search(pattern, options = {}) {
|
|
75921
|
+
const { caseSensitive = false, maxMatches = 1e3 } = options;
|
|
75922
|
+
const matches = [];
|
|
75923
|
+
let regex;
|
|
75924
|
+
if (pattern instanceof RegExp) {
|
|
75925
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
|
|
75926
|
+
regex = new RegExp(pattern.source, flags);
|
|
75927
|
+
} else if (typeof pattern === "string") {
|
|
75928
|
+
if (pattern.length === 0) return matches;
|
|
75929
|
+
const flexiblePattern = SearchIndex.toFlexiblePattern(pattern);
|
|
75930
|
+
if (flexiblePattern.length === 0) return matches;
|
|
75931
|
+
const flags = caseSensitive ? "g" : "gi";
|
|
75932
|
+
regex = new RegExp(flexiblePattern, flags);
|
|
75933
|
+
} else {
|
|
75934
|
+
return matches;
|
|
75935
|
+
}
|
|
75936
|
+
let match;
|
|
75937
|
+
while ((match = regex.exec(this.text)) !== null && matches.length < maxMatches) {
|
|
75938
|
+
matches.push({
|
|
75939
|
+
start: match.index,
|
|
75940
|
+
end: match.index + match[0].length,
|
|
75941
|
+
text: match[0]
|
|
75942
|
+
});
|
|
75943
|
+
if (match[0].length === 0) {
|
|
75944
|
+
regex.lastIndex++;
|
|
75945
|
+
}
|
|
75946
|
+
}
|
|
75947
|
+
return matches;
|
|
75533
75948
|
}
|
|
75534
|
-
const highlight = typeof options?.highlight === "boolean" ? options.highlight : true;
|
|
75535
|
-
return tr.setMeta(searchKey, { query, range, highlight });
|
|
75536
75949
|
}
|
|
75950
|
+
const customSearchHighlightsKey = new PluginKey("customSearchHighlights");
|
|
75537
75951
|
const isRegExp = (value) => Object.prototype.toString.call(value) === "[object RegExp]";
|
|
75952
|
+
const resolveInlineTextPosition = (doc2, position, direction) => {
|
|
75953
|
+
const docSize = doc2.content.size;
|
|
75954
|
+
if (!Number.isFinite(position) || position < 0 || position > docSize) {
|
|
75955
|
+
return position;
|
|
75956
|
+
}
|
|
75957
|
+
const step = direction === "forward" ? 1 : -1;
|
|
75958
|
+
let current = position;
|
|
75959
|
+
let iterations = 0;
|
|
75960
|
+
while (iterations < 8) {
|
|
75961
|
+
iterations += 1;
|
|
75962
|
+
const resolved = doc2.resolve(current);
|
|
75963
|
+
const boundaryNode = direction === "forward" ? resolved.nodeAfter : resolved.nodeBefore;
|
|
75964
|
+
if (!boundaryNode) break;
|
|
75965
|
+
if (boundaryNode.isText) break;
|
|
75966
|
+
if (!boundaryNode.isInline || boundaryNode.isAtom || boundaryNode.content.size === 0) break;
|
|
75967
|
+
const next = current + step;
|
|
75968
|
+
if (next < 0 || next > docSize) break;
|
|
75969
|
+
current = next;
|
|
75970
|
+
const adjacent = doc2.resolve(current);
|
|
75971
|
+
const checkNode = direction === "forward" ? adjacent.nodeAfter : adjacent.nodeBefore;
|
|
75972
|
+
if (checkNode && checkNode.isText) break;
|
|
75973
|
+
}
|
|
75974
|
+
return current;
|
|
75975
|
+
};
|
|
75976
|
+
const resolveSearchRange = ({ doc: doc2, from: from3, to, expectedText, highlights }) => {
|
|
75977
|
+
const docSize = doc2.content.size;
|
|
75978
|
+
let resolvedFrom = Math.max(0, Math.min(from3, docSize));
|
|
75979
|
+
let resolvedTo = Math.max(0, Math.min(to, docSize));
|
|
75980
|
+
if (highlights) {
|
|
75981
|
+
const windowStart = Math.max(0, resolvedFrom - 4);
|
|
75982
|
+
const windowEnd = Math.min(docSize, resolvedTo + 4);
|
|
75983
|
+
const candidates = highlights.find(windowStart, windowEnd);
|
|
75984
|
+
if (candidates.length > 0) {
|
|
75985
|
+
let chosen = candidates[0];
|
|
75986
|
+
if (expectedText) {
|
|
75987
|
+
const matching = candidates.filter(
|
|
75988
|
+
(decoration) => doc2.textBetween(decoration.from, decoration.to) === expectedText
|
|
75989
|
+
);
|
|
75990
|
+
if (matching.length > 0) {
|
|
75991
|
+
chosen = matching[0];
|
|
75992
|
+
}
|
|
75993
|
+
}
|
|
75994
|
+
resolvedFrom = chosen.from;
|
|
75995
|
+
resolvedTo = chosen.to;
|
|
75996
|
+
}
|
|
75997
|
+
}
|
|
75998
|
+
const normalizedFrom = resolveInlineTextPosition(doc2, resolvedFrom, "forward");
|
|
75999
|
+
const normalizedTo = resolveInlineTextPosition(doc2, resolvedTo, "backward");
|
|
76000
|
+
if (Number.isFinite(normalizedFrom) && Number.isFinite(normalizedTo) && normalizedFrom <= normalizedTo) {
|
|
76001
|
+
resolvedFrom = normalizedFrom;
|
|
76002
|
+
resolvedTo = normalizedTo;
|
|
76003
|
+
}
|
|
76004
|
+
return { from: resolvedFrom, to: resolvedTo };
|
|
76005
|
+
};
|
|
76006
|
+
const getPositionTracker = (editor) => {
|
|
76007
|
+
if (!editor) return null;
|
|
76008
|
+
if (editor.positionTracker) return editor.positionTracker;
|
|
76009
|
+
const storageTracker = editor.storage?.positionTracker?.tracker;
|
|
76010
|
+
if (storageTracker) {
|
|
76011
|
+
editor.positionTracker = storageTracker;
|
|
76012
|
+
return storageTracker;
|
|
76013
|
+
}
|
|
76014
|
+
const tracker = new PositionTracker(editor);
|
|
76015
|
+
if (editor.storage?.positionTracker) {
|
|
76016
|
+
editor.storage.positionTracker.tracker = tracker;
|
|
76017
|
+
}
|
|
76018
|
+
editor.positionTracker = tracker;
|
|
76019
|
+
return tracker;
|
|
76020
|
+
};
|
|
75538
76021
|
const Search = Extension.create({
|
|
75539
76022
|
// @ts-expect-error - Storage type mismatch will be fixed in TS migration
|
|
75540
76023
|
addStorage() {
|
|
@@ -75543,29 +76026,58 @@ const Search = Extension.create({
|
|
|
75543
76026
|
* @private
|
|
75544
76027
|
* @type {SearchMatch[]|null}
|
|
75545
76028
|
*/
|
|
75546
|
-
searchResults: []
|
|
76029
|
+
searchResults: [],
|
|
76030
|
+
/**
|
|
76031
|
+
* @private
|
|
76032
|
+
* @type {boolean}
|
|
76033
|
+
* Whether to apply CSS highlight classes to matches
|
|
76034
|
+
*/
|
|
76035
|
+
highlightEnabled: true,
|
|
76036
|
+
/**
|
|
76037
|
+
* @private
|
|
76038
|
+
* @type {SearchIndex}
|
|
76039
|
+
* Lazily-built search index for cross-paragraph matching
|
|
76040
|
+
*/
|
|
76041
|
+
searchIndex: new SearchIndex()
|
|
75547
76042
|
};
|
|
75548
76043
|
},
|
|
75549
76044
|
addPmPlugins() {
|
|
75550
76045
|
const editor = this.editor;
|
|
75551
76046
|
const storage = this.storage;
|
|
76047
|
+
const searchIndexInvalidatorPlugin = new Plugin({
|
|
76048
|
+
key: new PluginKey("searchIndexInvalidator"),
|
|
76049
|
+
appendTransaction(transactions, oldState, newState) {
|
|
76050
|
+
const docChanged = transactions.some((tr) => tr.docChanged);
|
|
76051
|
+
if (docChanged && storage?.searchIndex) {
|
|
76052
|
+
storage.searchIndex.invalidate();
|
|
76053
|
+
}
|
|
76054
|
+
return null;
|
|
76055
|
+
}
|
|
76056
|
+
});
|
|
75552
76057
|
const searchHighlightWithIdPlugin = new Plugin({
|
|
75553
|
-
key:
|
|
76058
|
+
key: customSearchHighlightsKey,
|
|
75554
76059
|
props: {
|
|
75555
76060
|
decorations(state) {
|
|
75556
76061
|
if (!editor) return null;
|
|
75557
76062
|
const matches = storage?.searchResults;
|
|
75558
76063
|
if (!matches?.length) return null;
|
|
75559
|
-
const
|
|
75560
|
-
|
|
75561
|
-
|
|
75562
|
-
}
|
|
75563
|
-
|
|
76064
|
+
const highlightEnabled = storage?.highlightEnabled !== false;
|
|
76065
|
+
const decorations = [];
|
|
76066
|
+
for (const match of matches) {
|
|
76067
|
+
const attrs = highlightEnabled ? { id: `search-match-${match.id}`, class: "ProseMirror-search-match" } : { id: `search-match-${match.id}` };
|
|
76068
|
+
if (match.ranges && match.ranges.length > 0) {
|
|
76069
|
+
for (const range of match.ranges) {
|
|
76070
|
+
decorations.push(Decoration.inline(range.from, range.to, attrs));
|
|
76071
|
+
}
|
|
76072
|
+
} else {
|
|
76073
|
+
decorations.push(Decoration.inline(match.from, match.to, attrs));
|
|
76074
|
+
}
|
|
76075
|
+
}
|
|
75564
76076
|
return DecorationSet.create(state.doc, decorations);
|
|
75565
76077
|
}
|
|
75566
76078
|
}
|
|
75567
76079
|
});
|
|
75568
|
-
return [search(), searchHighlightWithIdPlugin];
|
|
76080
|
+
return [search(), searchIndexInvalidatorPlugin, searchHighlightWithIdPlugin];
|
|
75569
76081
|
},
|
|
75570
76082
|
addCommands() {
|
|
75571
76083
|
return {
|
|
@@ -75579,21 +76091,51 @@ const Search = Extension.create({
|
|
|
75579
76091
|
goToFirstMatch: () => (
|
|
75580
76092
|
/** @returns {boolean} */
|
|
75581
76093
|
({ state, editor, dispatch }) => {
|
|
76094
|
+
const searchResults = this.storage?.searchResults;
|
|
76095
|
+
if (Array.isArray(searchResults) && searchResults.length > 0) {
|
|
76096
|
+
const firstMatch = searchResults[0];
|
|
76097
|
+
const from3 = firstMatch.ranges?.[0]?.from ?? firstMatch.from;
|
|
76098
|
+
const to = firstMatch.ranges?.[0]?.to ?? firstMatch.to;
|
|
76099
|
+
if (typeof from3 !== "number" || typeof to !== "number") {
|
|
76100
|
+
return false;
|
|
76101
|
+
}
|
|
76102
|
+
editor.view.focus();
|
|
76103
|
+
const tr2 = state.tr.setSelection(TextSelection$1.create(state.doc, from3, to)).scrollIntoView();
|
|
76104
|
+
if (dispatch) dispatch(tr2);
|
|
76105
|
+
const presentationEditor2 = editor.presentationEditor;
|
|
76106
|
+
if (presentationEditor2 && typeof presentationEditor2.scrollToPosition === "function") {
|
|
76107
|
+
const didScroll = presentationEditor2.scrollToPosition(from3, { block: "center" });
|
|
76108
|
+
if (didScroll) return true;
|
|
76109
|
+
}
|
|
76110
|
+
try {
|
|
76111
|
+
const domPos = editor.view.domAtPos(from3);
|
|
76112
|
+
if (domPos?.node?.scrollIntoView) {
|
|
76113
|
+
domPos.node.scrollIntoView(true);
|
|
76114
|
+
}
|
|
76115
|
+
} catch {
|
|
76116
|
+
}
|
|
76117
|
+
return true;
|
|
76118
|
+
}
|
|
75582
76119
|
const highlights = getMatchHighlights(state);
|
|
75583
76120
|
if (!highlights) return false;
|
|
75584
76121
|
const decorations = highlights.find();
|
|
75585
76122
|
if (!decorations?.length) return false;
|
|
75586
|
-
const
|
|
76123
|
+
const firstDeco = decorations[0];
|
|
75587
76124
|
editor.view.focus();
|
|
75588
|
-
const tr = state.tr.setSelection(TextSelection$1.create(state.doc,
|
|
76125
|
+
const tr = state.tr.setSelection(TextSelection$1.create(state.doc, firstDeco.from, firstDeco.to)).scrollIntoView();
|
|
75589
76126
|
if (dispatch) dispatch(tr);
|
|
75590
76127
|
const presentationEditor = editor.presentationEditor;
|
|
75591
76128
|
if (presentationEditor && typeof presentationEditor.scrollToPosition === "function") {
|
|
75592
|
-
const didScroll = presentationEditor.scrollToPosition(
|
|
76129
|
+
const didScroll = presentationEditor.scrollToPosition(firstDeco.from, { block: "center" });
|
|
75593
76130
|
if (didScroll) return true;
|
|
75594
76131
|
}
|
|
75595
|
-
|
|
75596
|
-
|
|
76132
|
+
try {
|
|
76133
|
+
const domPos = editor.view.domAtPos(firstDeco.from);
|
|
76134
|
+
if (domPos?.node?.scrollIntoView) {
|
|
76135
|
+
domPos.node.scrollIntoView(true);
|
|
76136
|
+
}
|
|
76137
|
+
} catch {
|
|
76138
|
+
}
|
|
75597
76139
|
return true;
|
|
75598
76140
|
}
|
|
75599
76141
|
),
|
|
@@ -75611,53 +76153,57 @@ const Search = Extension.create({
|
|
|
75611
76153
|
*
|
|
75612
76154
|
* // Search without visual highlighting
|
|
75613
76155
|
* const silentMatches = editor.commands.search('test', { highlight: false })
|
|
75614
|
-
*
|
|
76156
|
+
*
|
|
76157
|
+
* // Cross-paragraph search (works by default for plain strings)
|
|
76158
|
+
* const crossParagraphMatches = editor.commands.search('end of paragraph start of next')
|
|
76159
|
+
* @note Returns array of SearchMatch objects with positions and IDs.
|
|
76160
|
+
* Plain string searches are whitespace-flexible and match across paragraphs.
|
|
76161
|
+
* Regex searches match exactly as specified.
|
|
75615
76162
|
*/
|
|
75616
76163
|
search: (patternInput, options = {}) => (
|
|
75617
76164
|
/** @returns {SearchMatch[]} */
|
|
75618
|
-
({ state, dispatch }) => {
|
|
76165
|
+
({ state, dispatch, editor }) => {
|
|
75619
76166
|
if (options != null && (typeof options !== "object" || Array.isArray(options))) {
|
|
75620
76167
|
throw new TypeError("Search options must be an object");
|
|
75621
76168
|
}
|
|
75622
76169
|
const highlight = typeof options?.highlight === "boolean" ? options.highlight : true;
|
|
75623
|
-
|
|
76170
|
+
const maxMatches = typeof options?.maxMatches === "number" ? options.maxMatches : 1e3;
|
|
75624
76171
|
let caseSensitive = false;
|
|
75625
|
-
let
|
|
75626
|
-
const wholeWord = false;
|
|
76172
|
+
let searchPattern = patternInput;
|
|
75627
76173
|
if (isRegExp(patternInput)) {
|
|
75628
|
-
|
|
75629
|
-
|
|
75630
|
-
patternInput
|
|
75631
|
-
);
|
|
75632
|
-
regexp = true;
|
|
75633
|
-
pattern = regexPattern.source;
|
|
75634
|
-
caseSensitive = !regexPattern.flags.includes("i");
|
|
76174
|
+
caseSensitive = !patternInput.flags.includes("i");
|
|
76175
|
+
searchPattern = patternInput;
|
|
75635
76176
|
} else if (typeof patternInput === "string" && /^\/(.+)\/([gimsuy]*)$/.test(patternInput)) {
|
|
75636
76177
|
const [, body, flags] = patternInput.match(/^\/(.+)\/([gimsuy]*)$/);
|
|
75637
|
-
regexp = true;
|
|
75638
|
-
pattern = body;
|
|
75639
76178
|
caseSensitive = !flags.includes("i");
|
|
76179
|
+
searchPattern = new RegExp(body, flags.includes("g") ? flags : flags + "g");
|
|
75640
76180
|
} else {
|
|
75641
|
-
|
|
76181
|
+
searchPattern = String(patternInput);
|
|
75642
76182
|
}
|
|
75643
|
-
const
|
|
75644
|
-
|
|
76183
|
+
const searchIndex = this.storage.searchIndex;
|
|
76184
|
+
searchIndex.ensureValid(state.doc);
|
|
76185
|
+
const indexMatches = searchIndex.search(searchPattern, {
|
|
75645
76186
|
caseSensitive,
|
|
75646
|
-
|
|
75647
|
-
wholeWord
|
|
76187
|
+
maxMatches
|
|
75648
76188
|
});
|
|
75649
|
-
const
|
|
75650
|
-
|
|
75651
|
-
|
|
75652
|
-
|
|
75653
|
-
|
|
75654
|
-
|
|
75655
|
-
|
|
75656
|
-
|
|
75657
|
-
|
|
75658
|
-
|
|
75659
|
-
|
|
76189
|
+
const resultMatches = [];
|
|
76190
|
+
for (const indexMatch of indexMatches) {
|
|
76191
|
+
const ranges = searchIndex.offsetRangeToDocRanges(indexMatch.start, indexMatch.end);
|
|
76192
|
+
if (ranges.length === 0) continue;
|
|
76193
|
+
const matchTexts = ranges.map((r2) => state.doc.textBetween(r2.from, r2.to));
|
|
76194
|
+
const combinedText = matchTexts.join("");
|
|
76195
|
+
const match = {
|
|
76196
|
+
from: ranges[0].from,
|
|
76197
|
+
to: ranges[ranges.length - 1].to,
|
|
76198
|
+
text: combinedText,
|
|
76199
|
+
id: v4(),
|
|
76200
|
+
ranges,
|
|
76201
|
+
trackerIds: []
|
|
76202
|
+
};
|
|
76203
|
+
resultMatches.push(match);
|
|
76204
|
+
}
|
|
75660
76205
|
this.storage.searchResults = resultMatches;
|
|
76206
|
+
this.storage.highlightEnabled = highlight;
|
|
75661
76207
|
return resultMatches;
|
|
75662
76208
|
}
|
|
75663
76209
|
),
|
|
@@ -75668,12 +76214,48 @@ const Search = Extension.create({
|
|
|
75668
76214
|
* @example
|
|
75669
76215
|
* const searchResults = editor.commands.search('test string')
|
|
75670
76216
|
* editor.commands.goToSearchResult(searchResults[3])
|
|
75671
|
-
* @note Scrolls to match and selects it
|
|
76217
|
+
* @note Scrolls to match and selects it. For multi-range matches (cross-paragraph),
|
|
76218
|
+
* selects the first range and scrolls to it.
|
|
75672
76219
|
*/
|
|
75673
76220
|
goToSearchResult: (match) => (
|
|
75674
76221
|
/** @returns {boolean} */
|
|
75675
76222
|
({ state, dispatch, editor }) => {
|
|
75676
|
-
const
|
|
76223
|
+
const positionTracker = getPositionTracker(editor);
|
|
76224
|
+
const doc2 = state.doc;
|
|
76225
|
+
const highlights = getMatchHighlights(state);
|
|
76226
|
+
let from3, to;
|
|
76227
|
+
if (match?.ranges && match.ranges.length > 0 && match?.trackerIds && match.trackerIds.length > 0) {
|
|
76228
|
+
if (positionTracker?.resolve && match.trackerIds[0]) {
|
|
76229
|
+
const resolved = positionTracker.resolve(match.trackerIds[0]);
|
|
76230
|
+
if (resolved) {
|
|
76231
|
+
from3 = resolved.from;
|
|
76232
|
+
to = resolved.to;
|
|
76233
|
+
}
|
|
76234
|
+
}
|
|
76235
|
+
if (from3 === void 0) {
|
|
76236
|
+
from3 = match.ranges[0].from;
|
|
76237
|
+
to = match.ranges[0].to;
|
|
76238
|
+
}
|
|
76239
|
+
} else {
|
|
76240
|
+
from3 = match.from;
|
|
76241
|
+
to = match.to;
|
|
76242
|
+
if (positionTracker?.resolve && match?.id) {
|
|
76243
|
+
const resolved = positionTracker.resolve(match.id);
|
|
76244
|
+
if (resolved) {
|
|
76245
|
+
from3 = resolved.from;
|
|
76246
|
+
to = resolved.to;
|
|
76247
|
+
}
|
|
76248
|
+
}
|
|
76249
|
+
}
|
|
76250
|
+
const normalized = resolveSearchRange({
|
|
76251
|
+
doc: doc2,
|
|
76252
|
+
from: from3,
|
|
76253
|
+
to,
|
|
76254
|
+
expectedText: match?.text ?? null,
|
|
76255
|
+
highlights
|
|
76256
|
+
});
|
|
76257
|
+
from3 = normalized.from;
|
|
76258
|
+
to = normalized.to;
|
|
75677
76259
|
editor.view.focus();
|
|
75678
76260
|
const tr = state.tr.setSelection(TextSelection$1.create(state.doc, from3, to)).scrollIntoView();
|
|
75679
76261
|
if (dispatch) dispatch(tr);
|