@harbour-enterprises/superdoc 1.6.0-next.1 → 1.6.0-next.3
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-DzppMlfu.es.js → PdfViewer-DpUAoC7K.es.js} +2 -2
- package/dist/chunks/{PdfViewer-Dr4K1JKD.cjs → PdfViewer-Dy4TDA_a.cjs} +2 -2
- package/dist/chunks/{SuperConverter-Cyn9peRO.es.js → SuperConverter-B4dnd1SO.es.js} +1 -1
- package/dist/chunks/{SuperConverter-DpKjVrPl.cjs → SuperConverter-UL6mfKlt.cjs} +1 -1
- package/dist/chunks/{index-C7KECpDt.es.js → index-BB7PV6qA.es.js} +720 -54
- package/dist/chunks/{index-CmZ15rIJ.cjs → index-CM_lykaH.cjs} +4 -4
- package/dist/chunks/{index-DrcLOCfC.es.js → index-DEYAgfRu.es.js} +4 -4
- package/dist/chunks/{index-Cto-XsBE.cjs → index-vSyQ2hF9.cjs} +720 -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 +721 -55
- package/dist/superdoc.umd.js.map +1 -1
- package/package.json +3 -3
package/dist/superdoc.umd.js
CHANGED
|
@@ -38097,7 +38097,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
38097
38097
|
static getStoredSuperdocVersion(docx) {
|
|
38098
38098
|
return SuperConverter.getStoredCustomProperty(docx, "SuperdocVersion");
|
|
38099
38099
|
}
|
|
38100
|
-
static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.6.0-next.
|
|
38100
|
+
static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.6.0-next.3") {
|
|
38101
38101
|
return SuperConverter.setStoredCustomProperty(docx, "SuperdocVersion", version2, false);
|
|
38102
38102
|
}
|
|
38103
38103
|
/**
|
|
@@ -47391,6 +47391,158 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
47391
47391
|
return Object.fromEntries(markEntries);
|
|
47392
47392
|
}
|
|
47393
47393
|
};
|
|
47394
|
+
const positionTrackerKey = new PluginKey("positionTracker");
|
|
47395
|
+
function createPositionTrackerPlugin() {
|
|
47396
|
+
return new Plugin({
|
|
47397
|
+
key: positionTrackerKey,
|
|
47398
|
+
state: {
|
|
47399
|
+
init() {
|
|
47400
|
+
return {
|
|
47401
|
+
decorations: DecorationSet.empty,
|
|
47402
|
+
generation: 0
|
|
47403
|
+
};
|
|
47404
|
+
},
|
|
47405
|
+
apply(tr, state) {
|
|
47406
|
+
let { decorations, generation } = state;
|
|
47407
|
+
const meta2 = tr.getMeta(positionTrackerKey);
|
|
47408
|
+
if (meta2?.action === "add") {
|
|
47409
|
+
decorations = decorations.add(tr.doc, meta2.decorations);
|
|
47410
|
+
} else if (meta2?.action === "remove") {
|
|
47411
|
+
const toRemove = decorations.find().filter((decoration) => meta2.ids.includes(decoration.spec.id));
|
|
47412
|
+
decorations = decorations.remove(toRemove);
|
|
47413
|
+
} else if (meta2?.action === "removeByType") {
|
|
47414
|
+
const toRemove = decorations.find().filter((decoration) => decoration.spec.type === meta2.type);
|
|
47415
|
+
decorations = decorations.remove(toRemove);
|
|
47416
|
+
}
|
|
47417
|
+
if (tr.docChanged) {
|
|
47418
|
+
decorations = decorations.map(tr.mapping, tr.doc);
|
|
47419
|
+
generation += 1;
|
|
47420
|
+
}
|
|
47421
|
+
return { decorations, generation };
|
|
47422
|
+
}
|
|
47423
|
+
},
|
|
47424
|
+
props: {
|
|
47425
|
+
decorations() {
|
|
47426
|
+
return DecorationSet.empty;
|
|
47427
|
+
}
|
|
47428
|
+
}
|
|
47429
|
+
});
|
|
47430
|
+
}
|
|
47431
|
+
class PositionTracker {
|
|
47432
|
+
#editor;
|
|
47433
|
+
constructor(editor) {
|
|
47434
|
+
this.#editor = editor;
|
|
47435
|
+
}
|
|
47436
|
+
#getState() {
|
|
47437
|
+
if (!this.#editor?.state) return null;
|
|
47438
|
+
return positionTrackerKey.getState(this.#editor.state) ?? null;
|
|
47439
|
+
}
|
|
47440
|
+
track(from2, to, spec) {
|
|
47441
|
+
const id = v4();
|
|
47442
|
+
if (!this.#editor?.state) return id;
|
|
47443
|
+
const fullSpec = { kind: "range", ...spec, id };
|
|
47444
|
+
const deco = Decoration.inline(from2, to, {}, fullSpec);
|
|
47445
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
47446
|
+
action: "add",
|
|
47447
|
+
decorations: [deco]
|
|
47448
|
+
}).setMeta("addToHistory", false);
|
|
47449
|
+
this.#editor.dispatch(tr);
|
|
47450
|
+
return id;
|
|
47451
|
+
}
|
|
47452
|
+
trackMany(ranges) {
|
|
47453
|
+
if (!this.#editor?.state) {
|
|
47454
|
+
return ranges.map(() => v4());
|
|
47455
|
+
}
|
|
47456
|
+
const ids = [];
|
|
47457
|
+
const decorations = [];
|
|
47458
|
+
for (const { from: from2, to, spec } of ranges) {
|
|
47459
|
+
const id = v4();
|
|
47460
|
+
ids.push(id);
|
|
47461
|
+
const fullSpec = { kind: "range", ...spec, id };
|
|
47462
|
+
decorations.push(Decoration.inline(from2, to, {}, fullSpec));
|
|
47463
|
+
}
|
|
47464
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
47465
|
+
action: "add",
|
|
47466
|
+
decorations
|
|
47467
|
+
}).setMeta("addToHistory", false);
|
|
47468
|
+
this.#editor.dispatch(tr);
|
|
47469
|
+
return ids;
|
|
47470
|
+
}
|
|
47471
|
+
untrack(id) {
|
|
47472
|
+
if (!this.#editor?.state) return;
|
|
47473
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
47474
|
+
action: "remove",
|
|
47475
|
+
ids: [id]
|
|
47476
|
+
}).setMeta("addToHistory", false);
|
|
47477
|
+
this.#editor.dispatch(tr);
|
|
47478
|
+
}
|
|
47479
|
+
untrackMany(ids) {
|
|
47480
|
+
if (!this.#editor?.state || ids.length === 0) return;
|
|
47481
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
47482
|
+
action: "remove",
|
|
47483
|
+
ids
|
|
47484
|
+
}).setMeta("addToHistory", false);
|
|
47485
|
+
this.#editor.dispatch(tr);
|
|
47486
|
+
}
|
|
47487
|
+
untrackByType(type) {
|
|
47488
|
+
if (!this.#editor?.state) return;
|
|
47489
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
47490
|
+
action: "removeByType",
|
|
47491
|
+
type
|
|
47492
|
+
}).setMeta("addToHistory", false);
|
|
47493
|
+
this.#editor.dispatch(tr);
|
|
47494
|
+
}
|
|
47495
|
+
resolve(id) {
|
|
47496
|
+
const state = this.#getState();
|
|
47497
|
+
if (!state) return null;
|
|
47498
|
+
const found2 = state.decorations.find().find((decoration) => decoration.spec.id === id);
|
|
47499
|
+
if (!found2) return null;
|
|
47500
|
+
const spec = found2.spec;
|
|
47501
|
+
return {
|
|
47502
|
+
id: spec.id,
|
|
47503
|
+
from: found2.from,
|
|
47504
|
+
to: found2.to,
|
|
47505
|
+
spec
|
|
47506
|
+
};
|
|
47507
|
+
}
|
|
47508
|
+
resolveMany(ids) {
|
|
47509
|
+
const result = /* @__PURE__ */ new Map();
|
|
47510
|
+
for (const id of ids) {
|
|
47511
|
+
result.set(id, null);
|
|
47512
|
+
}
|
|
47513
|
+
const state = this.#getState();
|
|
47514
|
+
if (!state || ids.length === 0) return result;
|
|
47515
|
+
const idSet = new Set(ids);
|
|
47516
|
+
for (const decoration of state.decorations.find()) {
|
|
47517
|
+
const spec = decoration.spec;
|
|
47518
|
+
if (idSet.has(spec.id)) {
|
|
47519
|
+
result.set(spec.id, {
|
|
47520
|
+
id: spec.id,
|
|
47521
|
+
from: decoration.from,
|
|
47522
|
+
to: decoration.to,
|
|
47523
|
+
spec
|
|
47524
|
+
});
|
|
47525
|
+
}
|
|
47526
|
+
}
|
|
47527
|
+
return result;
|
|
47528
|
+
}
|
|
47529
|
+
findByType(type) {
|
|
47530
|
+
const state = this.#getState();
|
|
47531
|
+
if (!state) return [];
|
|
47532
|
+
return state.decorations.find().filter((decoration) => decoration.spec.type === type).map((decoration) => {
|
|
47533
|
+
const spec = decoration.spec;
|
|
47534
|
+
return {
|
|
47535
|
+
id: spec.id,
|
|
47536
|
+
from: decoration.from,
|
|
47537
|
+
to: decoration.to,
|
|
47538
|
+
spec
|
|
47539
|
+
};
|
|
47540
|
+
});
|
|
47541
|
+
}
|
|
47542
|
+
get generation() {
|
|
47543
|
+
return this.#getState()?.generation ?? 0;
|
|
47544
|
+
}
|
|
47545
|
+
}
|
|
47394
47546
|
class OxmlNode extends Node$2 {
|
|
47395
47547
|
constructor(config2) {
|
|
47396
47548
|
super(config2);
|
|
@@ -49751,6 +49903,34 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
49751
49903
|
return [editorFocusPlugin];
|
|
49752
49904
|
}
|
|
49753
49905
|
});
|
|
49906
|
+
const PositionTrackerExtension = Extension.create({
|
|
49907
|
+
name: "positionTracker",
|
|
49908
|
+
addStorage() {
|
|
49909
|
+
return {
|
|
49910
|
+
tracker: null
|
|
49911
|
+
};
|
|
49912
|
+
},
|
|
49913
|
+
addPmPlugins() {
|
|
49914
|
+
return [createPositionTrackerPlugin()];
|
|
49915
|
+
},
|
|
49916
|
+
onCreate() {
|
|
49917
|
+
const existing = this.editor?.positionTracker ?? this.storage.tracker;
|
|
49918
|
+
if (existing) {
|
|
49919
|
+
this.storage.tracker = existing;
|
|
49920
|
+
this.editor.positionTracker = existing;
|
|
49921
|
+
return;
|
|
49922
|
+
}
|
|
49923
|
+
const tracker = new PositionTracker(this.editor);
|
|
49924
|
+
this.storage.tracker = tracker;
|
|
49925
|
+
this.editor.positionTracker = tracker;
|
|
49926
|
+
},
|
|
49927
|
+
onDestroy() {
|
|
49928
|
+
if (this.editor?.positionTracker === this.storage.tracker) {
|
|
49929
|
+
this.editor.positionTracker = null;
|
|
49930
|
+
}
|
|
49931
|
+
this.storage.tracker = null;
|
|
49932
|
+
}
|
|
49933
|
+
});
|
|
49754
49934
|
let EventEmitter$1 = class EventEmitter {
|
|
49755
49935
|
#events = /* @__PURE__ */ new Map();
|
|
49756
49936
|
/**
|
|
@@ -51085,6 +51265,90 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
51085
51265
|
name: "comments",
|
|
51086
51266
|
addCommands() {
|
|
51087
51267
|
return {
|
|
51268
|
+
/**
|
|
51269
|
+
* Add a comment to the current selection
|
|
51270
|
+
* @category Command
|
|
51271
|
+
* @param {string|Object} contentOrOptions - Comment content as a string, or an options object
|
|
51272
|
+
* @param {string} [contentOrOptions.content] - The comment content (text or HTML)
|
|
51273
|
+
* @param {string} [contentOrOptions.author] - Author name (defaults to user from editor config)
|
|
51274
|
+
* @param {string} [contentOrOptions.authorEmail] - Author email (defaults to user from editor config)
|
|
51275
|
+
* @param {string} [contentOrOptions.authorImage] - Author image URL (defaults to user from editor config)
|
|
51276
|
+
* @param {boolean} [contentOrOptions.isInternal=false] - Whether the comment is internal/private
|
|
51277
|
+
* @returns {boolean} True if the comment was added successfully, false otherwise
|
|
51278
|
+
* @example
|
|
51279
|
+
* // Simple usage with just content
|
|
51280
|
+
* editor.commands.addComment('This needs review')
|
|
51281
|
+
*
|
|
51282
|
+
* // With options
|
|
51283
|
+
* editor.commands.addComment({
|
|
51284
|
+
* content: 'Please clarify this section',
|
|
51285
|
+
* author: 'Jane Doe',
|
|
51286
|
+
* isInternal: true
|
|
51287
|
+
* })
|
|
51288
|
+
*
|
|
51289
|
+
* // To get the comment ID, listen to the commentsUpdate event
|
|
51290
|
+
* editor.on('commentsUpdate', (event) => {
|
|
51291
|
+
* if (event.type === 'add') {
|
|
51292
|
+
* console.log('New comment ID:', event.activeCommentId)
|
|
51293
|
+
* }
|
|
51294
|
+
* })
|
|
51295
|
+
*/
|
|
51296
|
+
addComment: (contentOrOptions) => ({ tr, dispatch, editor }) => {
|
|
51297
|
+
const { selection } = tr;
|
|
51298
|
+
const { $from, $to } = selection;
|
|
51299
|
+
if ($from.pos === $to.pos) {
|
|
51300
|
+
console.warn("addComment requires a text selection. Please select text before adding a comment.");
|
|
51301
|
+
return false;
|
|
51302
|
+
}
|
|
51303
|
+
let content2, author, authorEmail, authorImage, isInternal;
|
|
51304
|
+
if (typeof contentOrOptions === "string") {
|
|
51305
|
+
content2 = contentOrOptions;
|
|
51306
|
+
} else if (contentOrOptions && typeof contentOrOptions === "object") {
|
|
51307
|
+
content2 = contentOrOptions.content;
|
|
51308
|
+
author = contentOrOptions.author;
|
|
51309
|
+
authorEmail = contentOrOptions.authorEmail;
|
|
51310
|
+
authorImage = contentOrOptions.authorImage;
|
|
51311
|
+
isInternal = contentOrOptions.isInternal;
|
|
51312
|
+
}
|
|
51313
|
+
const commentId = v4();
|
|
51314
|
+
const resolvedInternal = isInternal ?? false;
|
|
51315
|
+
const configUser = editor.options?.user || {};
|
|
51316
|
+
tr.setMeta(CommentsPluginKey, { event: "add" });
|
|
51317
|
+
tr.addMark(
|
|
51318
|
+
$from.pos,
|
|
51319
|
+
$to.pos,
|
|
51320
|
+
editor.schema.marks[CommentMarkName$1].create({
|
|
51321
|
+
commentId,
|
|
51322
|
+
internal: resolvedInternal
|
|
51323
|
+
})
|
|
51324
|
+
);
|
|
51325
|
+
if (dispatch) dispatch(tr);
|
|
51326
|
+
const commentPayload = normalizeCommentEventPayload({
|
|
51327
|
+
conversation: {
|
|
51328
|
+
commentId,
|
|
51329
|
+
isInternal: resolvedInternal,
|
|
51330
|
+
commentText: content2,
|
|
51331
|
+
creatorName: author ?? configUser.name,
|
|
51332
|
+
creatorEmail: authorEmail ?? configUser.email,
|
|
51333
|
+
creatorImage: authorImage ?? configUser.image,
|
|
51334
|
+
createdTime: Date.now()
|
|
51335
|
+
},
|
|
51336
|
+
editorOptions: editor.options,
|
|
51337
|
+
fallbackCommentId: commentId,
|
|
51338
|
+
fallbackInternal: resolvedInternal
|
|
51339
|
+
});
|
|
51340
|
+
editor.emit("commentsUpdate", {
|
|
51341
|
+
type: comments_module_events.ADD,
|
|
51342
|
+
comment: commentPayload,
|
|
51343
|
+
activeCommentId: commentId
|
|
51344
|
+
});
|
|
51345
|
+
return true;
|
|
51346
|
+
},
|
|
51347
|
+
/**
|
|
51348
|
+
* @private
|
|
51349
|
+
* Internal command to insert a comment mark at the current selection.
|
|
51350
|
+
* Use `addComment` for the public API.
|
|
51351
|
+
*/
|
|
51088
51352
|
insertComment: (conversation = {}) => ({ tr, dispatch }) => {
|
|
51089
51353
|
const { selection } = tr;
|
|
51090
51354
|
const { $from, $to } = selection;
|
|
@@ -64003,7 +64267,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
64003
64267
|
return false;
|
|
64004
64268
|
}
|
|
64005
64269
|
};
|
|
64006
|
-
const summaryVersion = "1.6.0-next.
|
|
64270
|
+
const summaryVersion = "1.6.0-next.3";
|
|
64007
64271
|
const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
|
|
64008
64272
|
const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
|
|
64009
64273
|
function mapAttributes(attrs) {
|
|
@@ -65497,7 +65761,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
65497
65761
|
*/
|
|
65498
65762
|
#createExtensionService() {
|
|
65499
65763
|
const allowedExtensions = ["extension", "node", "mark"];
|
|
65500
|
-
const coreExtensions = [Editable, Commands, EditorFocus, Keymap];
|
|
65764
|
+
const coreExtensions = [Editable, Commands, EditorFocus, Keymap, PositionTrackerExtension];
|
|
65501
65765
|
const externalExtensions = this.options.externalExtensions || [];
|
|
65502
65766
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter((extension) => {
|
|
65503
65767
|
const extensionType = typeof extension?.type === "string" ? extension.type : void 0;
|
|
@@ -66671,7 +66935,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
66671
66935
|
* Process collaboration migrations
|
|
66672
66936
|
*/
|
|
66673
66937
|
processCollaborationMigrations() {
|
|
66674
|
-
console.debug("[checkVersionMigrations] Current editor version", "1.6.0-next.
|
|
66938
|
+
console.debug("[checkVersionMigrations] Current editor version", "1.6.0-next.3");
|
|
66675
66939
|
if (!this.options.ydoc) return;
|
|
66676
66940
|
const metaMap = this.options.ydoc.getMap("meta");
|
|
66677
66941
|
let docVersion = metaMap.get("version");
|
|
@@ -123801,14 +124065,317 @@ ${o}
|
|
|
123801
124065
|
let search2 = searchKey.getState(state);
|
|
123802
124066
|
return search2 ? search2.deco : DecorationSet.empty;
|
|
123803
124067
|
}
|
|
123804
|
-
|
|
123805
|
-
|
|
123806
|
-
|
|
124068
|
+
const BLOCK_SEPARATOR = "\n";
|
|
124069
|
+
const ATOM_PLACEHOLDER = "";
|
|
124070
|
+
class SearchIndex {
|
|
124071
|
+
/** @type {string} */
|
|
124072
|
+
text = "";
|
|
124073
|
+
/** @type {Segment[]} */
|
|
124074
|
+
segments = [];
|
|
124075
|
+
/** @type {boolean} */
|
|
124076
|
+
valid = false;
|
|
124077
|
+
/** @type {number} */
|
|
124078
|
+
docSize = 0;
|
|
124079
|
+
/**
|
|
124080
|
+
* Build the search index from a ProseMirror document.
|
|
124081
|
+
* Uses doc.textBetween for the flattened string and walks
|
|
124082
|
+
* the document to build the segment offset map.
|
|
124083
|
+
*
|
|
124084
|
+
* @param {import('prosemirror-model').Node} doc - The ProseMirror document
|
|
124085
|
+
*/
|
|
124086
|
+
build(doc2) {
|
|
124087
|
+
this.text = doc2.textBetween(0, doc2.content.size, BLOCK_SEPARATOR, ATOM_PLACEHOLDER);
|
|
124088
|
+
this.segments = [];
|
|
124089
|
+
this.docSize = doc2.content.size;
|
|
124090
|
+
let offset2 = 0;
|
|
124091
|
+
this.#walkNodeContent(doc2, 0, offset2, (segment) => {
|
|
124092
|
+
this.segments.push(segment);
|
|
124093
|
+
offset2 = segment.offsetEnd;
|
|
124094
|
+
});
|
|
124095
|
+
this.valid = true;
|
|
124096
|
+
}
|
|
124097
|
+
/**
|
|
124098
|
+
* Walk the content of a node to build segments.
|
|
124099
|
+
* This method processes the children of a node, given the position
|
|
124100
|
+
* where the node's content starts.
|
|
124101
|
+
*
|
|
124102
|
+
* @param {import('prosemirror-model').Node} node - Current node
|
|
124103
|
+
* @param {number} contentStart - Document position where this node's content starts
|
|
124104
|
+
* @param {number} offset - Current offset in flattened string
|
|
124105
|
+
* @param {(segment: Segment) => void} addSegment - Callback to add a segment
|
|
124106
|
+
* @returns {number} The new offset after processing this node's content
|
|
124107
|
+
*/
|
|
124108
|
+
#walkNodeContent(node2, contentStart, offset2, addSegment) {
|
|
124109
|
+
let currentOffset = offset2;
|
|
124110
|
+
let isFirstChild = true;
|
|
124111
|
+
node2.forEach((child, childContentOffset) => {
|
|
124112
|
+
const childDocPos = contentStart + childContentOffset;
|
|
124113
|
+
if (child.isBlock && !isFirstChild) {
|
|
124114
|
+
addSegment({
|
|
124115
|
+
offsetStart: currentOffset,
|
|
124116
|
+
offsetEnd: currentOffset + 1,
|
|
124117
|
+
docFrom: childDocPos,
|
|
124118
|
+
docTo: childDocPos,
|
|
124119
|
+
kind: "blockSep"
|
|
124120
|
+
});
|
|
124121
|
+
currentOffset += 1;
|
|
124122
|
+
}
|
|
124123
|
+
currentOffset = this.#walkNode(child, childDocPos, currentOffset, addSegment);
|
|
124124
|
+
isFirstChild = false;
|
|
124125
|
+
});
|
|
124126
|
+
return currentOffset;
|
|
124127
|
+
}
|
|
124128
|
+
/**
|
|
124129
|
+
* Recursively walk a node and its descendants to build segments.
|
|
124130
|
+
*
|
|
124131
|
+
* @param {import('prosemirror-model').Node} node - Current node
|
|
124132
|
+
* @param {number} docPos - Document position at start of this node
|
|
124133
|
+
* @param {number} offset - Current offset in flattened string
|
|
124134
|
+
* @param {(segment: Segment) => void} addSegment - Callback to add a segment
|
|
124135
|
+
* @returns {number} The new offset after processing this node
|
|
124136
|
+
*/
|
|
124137
|
+
#walkNode(node2, docPos, offset2, addSegment) {
|
|
124138
|
+
if (node2.isText) {
|
|
124139
|
+
const text2 = node2.text || "";
|
|
124140
|
+
if (text2.length > 0) {
|
|
124141
|
+
addSegment({
|
|
124142
|
+
offsetStart: offset2,
|
|
124143
|
+
offsetEnd: offset2 + text2.length,
|
|
124144
|
+
docFrom: docPos,
|
|
124145
|
+
docTo: docPos + text2.length,
|
|
124146
|
+
kind: "text"
|
|
124147
|
+
});
|
|
124148
|
+
return offset2 + text2.length;
|
|
124149
|
+
}
|
|
124150
|
+
return offset2;
|
|
124151
|
+
}
|
|
124152
|
+
if (node2.isLeaf) {
|
|
124153
|
+
if (node2.type.name === "hard_break") {
|
|
124154
|
+
addSegment({
|
|
124155
|
+
offsetStart: offset2,
|
|
124156
|
+
offsetEnd: offset2 + 1,
|
|
124157
|
+
docFrom: docPos,
|
|
124158
|
+
docTo: docPos + node2.nodeSize,
|
|
124159
|
+
kind: "hardBreak"
|
|
124160
|
+
});
|
|
124161
|
+
return offset2 + 1;
|
|
124162
|
+
}
|
|
124163
|
+
addSegment({
|
|
124164
|
+
offsetStart: offset2,
|
|
124165
|
+
offsetEnd: offset2 + 1,
|
|
124166
|
+
docFrom: docPos,
|
|
124167
|
+
docTo: docPos + node2.nodeSize,
|
|
124168
|
+
kind: "atom"
|
|
124169
|
+
});
|
|
124170
|
+
return offset2 + 1;
|
|
124171
|
+
}
|
|
124172
|
+
return this.#walkNodeContent(node2, docPos + 1, offset2, addSegment);
|
|
124173
|
+
}
|
|
124174
|
+
/**
|
|
124175
|
+
* Mark the index as stale. It will be rebuilt on next search.
|
|
124176
|
+
*/
|
|
124177
|
+
invalidate() {
|
|
124178
|
+
this.valid = false;
|
|
124179
|
+
}
|
|
124180
|
+
/**
|
|
124181
|
+
* Check if the index needs rebuilding for the given document.
|
|
124182
|
+
*
|
|
124183
|
+
* @param {import('prosemirror-model').Node} doc - The document to check against
|
|
124184
|
+
* @returns {boolean} True if index is stale and needs rebuilding
|
|
124185
|
+
*/
|
|
124186
|
+
isStale(doc2) {
|
|
124187
|
+
return !this.valid || doc2.content.size !== this.docSize;
|
|
124188
|
+
}
|
|
124189
|
+
/**
|
|
124190
|
+
* Ensure the index is valid for the given document.
|
|
124191
|
+
* Rebuilds if stale.
|
|
124192
|
+
*
|
|
124193
|
+
* @param {import('prosemirror-model').Node} doc - The document
|
|
124194
|
+
*/
|
|
124195
|
+
ensureValid(doc2) {
|
|
124196
|
+
if (this.isStale(doc2)) {
|
|
124197
|
+
this.build(doc2);
|
|
124198
|
+
}
|
|
124199
|
+
}
|
|
124200
|
+
/**
|
|
124201
|
+
* Convert an offset range in the flattened string to document ranges.
|
|
124202
|
+
* Skips separator/atom segments and returns only text ranges.
|
|
124203
|
+
*
|
|
124204
|
+
* @param {number} start - Start offset in flattened string
|
|
124205
|
+
* @param {number} end - End offset in flattened string
|
|
124206
|
+
* @returns {DocRange[]} Array of document ranges (text segments only)
|
|
124207
|
+
*/
|
|
124208
|
+
offsetRangeToDocRanges(start2, end2) {
|
|
124209
|
+
const ranges = [];
|
|
124210
|
+
for (const segment of this.segments) {
|
|
124211
|
+
if (segment.offsetEnd <= start2) continue;
|
|
124212
|
+
if (segment.offsetStart >= end2) break;
|
|
124213
|
+
if (segment.kind !== "text") continue;
|
|
124214
|
+
const overlapStart = Math.max(start2, segment.offsetStart);
|
|
124215
|
+
const overlapEnd = Math.min(end2, segment.offsetEnd);
|
|
124216
|
+
if (overlapStart < overlapEnd) {
|
|
124217
|
+
const startInSegment = overlapStart - segment.offsetStart;
|
|
124218
|
+
const endInSegment = overlapEnd - segment.offsetStart;
|
|
124219
|
+
ranges.push({
|
|
124220
|
+
from: segment.docFrom + startInSegment,
|
|
124221
|
+
to: segment.docFrom + endInSegment
|
|
124222
|
+
});
|
|
124223
|
+
}
|
|
124224
|
+
}
|
|
124225
|
+
return ranges;
|
|
124226
|
+
}
|
|
124227
|
+
/**
|
|
124228
|
+
* Find the document position for a given offset in the flattened string.
|
|
124229
|
+
*
|
|
124230
|
+
* @param {number} offset - Offset in flattened string
|
|
124231
|
+
* @returns {number|null} Document position, or null if not found
|
|
124232
|
+
*/
|
|
124233
|
+
offsetToDocPos(offset2) {
|
|
124234
|
+
for (const segment of this.segments) {
|
|
124235
|
+
if (offset2 >= segment.offsetStart && offset2 < segment.offsetEnd) {
|
|
124236
|
+
if (segment.kind === "text") {
|
|
124237
|
+
return segment.docFrom + (offset2 - segment.offsetStart);
|
|
124238
|
+
}
|
|
124239
|
+
return segment.docFrom;
|
|
124240
|
+
}
|
|
124241
|
+
}
|
|
124242
|
+
if (this.segments.length > 0 && offset2 === this.segments[this.segments.length - 1].offsetEnd) {
|
|
124243
|
+
const lastSeg = this.segments[this.segments.length - 1];
|
|
124244
|
+
return lastSeg.docTo;
|
|
124245
|
+
}
|
|
124246
|
+
return null;
|
|
124247
|
+
}
|
|
124248
|
+
/**
|
|
124249
|
+
* Escape special regex characters in a string.
|
|
124250
|
+
*
|
|
124251
|
+
* @param {string} str - String to escape
|
|
124252
|
+
* @returns {string} Escaped string safe for use in RegExp
|
|
124253
|
+
*/
|
|
124254
|
+
static escapeRegex(str) {
|
|
124255
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
124256
|
+
}
|
|
124257
|
+
/**
|
|
124258
|
+
* Convert a plain search string to a whitespace-flexible regex pattern.
|
|
124259
|
+
* This allows matching across paragraph boundaries.
|
|
124260
|
+
*
|
|
124261
|
+
* @param {string} searchString - The search string
|
|
124262
|
+
* @returns {string} Regex pattern string
|
|
124263
|
+
*/
|
|
124264
|
+
static toFlexiblePattern(searchString) {
|
|
124265
|
+
const parts = searchString.split(/\s+/).filter((part) => part.length > 0);
|
|
124266
|
+
if (parts.length === 0) return "";
|
|
124267
|
+
return parts.map((part) => SearchIndex.escapeRegex(part)).join("\\s+");
|
|
124268
|
+
}
|
|
124269
|
+
/**
|
|
124270
|
+
* Search the index for matches.
|
|
124271
|
+
*
|
|
124272
|
+
* @param {string | RegExp} pattern - Search pattern (string or regex)
|
|
124273
|
+
* @param {Object} options - Search options
|
|
124274
|
+
* @param {boolean} [options.caseSensitive=false] - Case sensitive search
|
|
124275
|
+
* @param {number} [options.maxMatches=1000] - Maximum number of matches to return
|
|
124276
|
+
* @returns {Array<{start: number, end: number, text: string}>} Array of matches with offsets
|
|
124277
|
+
*/
|
|
124278
|
+
search(pattern, options = {}) {
|
|
124279
|
+
const { caseSensitive = false, maxMatches = 1e3 } = options;
|
|
124280
|
+
const matches2 = [];
|
|
124281
|
+
let regex;
|
|
124282
|
+
if (pattern instanceof RegExp) {
|
|
124283
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
|
|
124284
|
+
regex = new RegExp(pattern.source, flags);
|
|
124285
|
+
} else if (typeof pattern === "string") {
|
|
124286
|
+
if (pattern.length === 0) return matches2;
|
|
124287
|
+
const flexiblePattern = SearchIndex.toFlexiblePattern(pattern);
|
|
124288
|
+
if (flexiblePattern.length === 0) return matches2;
|
|
124289
|
+
const flags = caseSensitive ? "g" : "gi";
|
|
124290
|
+
regex = new RegExp(flexiblePattern, flags);
|
|
124291
|
+
} else {
|
|
124292
|
+
return matches2;
|
|
124293
|
+
}
|
|
124294
|
+
let match;
|
|
124295
|
+
while ((match = regex.exec(this.text)) !== null && matches2.length < maxMatches) {
|
|
124296
|
+
matches2.push({
|
|
124297
|
+
start: match.index,
|
|
124298
|
+
end: match.index + match[0].length,
|
|
124299
|
+
text: match[0]
|
|
124300
|
+
});
|
|
124301
|
+
if (match[0].length === 0) {
|
|
124302
|
+
regex.lastIndex++;
|
|
124303
|
+
}
|
|
124304
|
+
}
|
|
124305
|
+
return matches2;
|
|
123807
124306
|
}
|
|
123808
|
-
const highlight = typeof options?.highlight === "boolean" ? options.highlight : true;
|
|
123809
|
-
return tr.setMeta(searchKey, { query, range: range2, highlight });
|
|
123810
124307
|
}
|
|
124308
|
+
const customSearchHighlightsKey = new PluginKey("customSearchHighlights");
|
|
123811
124309
|
const isRegExp = (value) => Object.prototype.toString.call(value) === "[object RegExp]";
|
|
124310
|
+
const resolveInlineTextPosition = (doc2, position2, direction) => {
|
|
124311
|
+
const docSize = doc2.content.size;
|
|
124312
|
+
if (!Number.isFinite(position2) || position2 < 0 || position2 > docSize) {
|
|
124313
|
+
return position2;
|
|
124314
|
+
}
|
|
124315
|
+
const step = direction === "forward" ? 1 : -1;
|
|
124316
|
+
let current = position2;
|
|
124317
|
+
let iterations = 0;
|
|
124318
|
+
while (iterations < 8) {
|
|
124319
|
+
iterations += 1;
|
|
124320
|
+
const resolved = doc2.resolve(current);
|
|
124321
|
+
const boundaryNode = direction === "forward" ? resolved.nodeAfter : resolved.nodeBefore;
|
|
124322
|
+
if (!boundaryNode) break;
|
|
124323
|
+
if (boundaryNode.isText) break;
|
|
124324
|
+
if (!boundaryNode.isInline || boundaryNode.isAtom || boundaryNode.content.size === 0) break;
|
|
124325
|
+
const next2 = current + step;
|
|
124326
|
+
if (next2 < 0 || next2 > docSize) break;
|
|
124327
|
+
current = next2;
|
|
124328
|
+
const adjacent = doc2.resolve(current);
|
|
124329
|
+
const checkNode = direction === "forward" ? adjacent.nodeAfter : adjacent.nodeBefore;
|
|
124330
|
+
if (checkNode && checkNode.isText) break;
|
|
124331
|
+
}
|
|
124332
|
+
return current;
|
|
124333
|
+
};
|
|
124334
|
+
const resolveSearchRange = ({ doc: doc2, from: from2, to, expectedText, highlights }) => {
|
|
124335
|
+
const docSize = doc2.content.size;
|
|
124336
|
+
let resolvedFrom = Math.max(0, Math.min(from2, docSize));
|
|
124337
|
+
let resolvedTo = Math.max(0, Math.min(to, docSize));
|
|
124338
|
+
if (highlights) {
|
|
124339
|
+
const windowStart = Math.max(0, resolvedFrom - 4);
|
|
124340
|
+
const windowEnd = Math.min(docSize, resolvedTo + 4);
|
|
124341
|
+
const candidates = highlights.find(windowStart, windowEnd);
|
|
124342
|
+
if (candidates.length > 0) {
|
|
124343
|
+
let chosen = candidates[0];
|
|
124344
|
+
if (expectedText) {
|
|
124345
|
+
const matching = candidates.filter(
|
|
124346
|
+
(decoration) => doc2.textBetween(decoration.from, decoration.to) === expectedText
|
|
124347
|
+
);
|
|
124348
|
+
if (matching.length > 0) {
|
|
124349
|
+
chosen = matching[0];
|
|
124350
|
+
}
|
|
124351
|
+
}
|
|
124352
|
+
resolvedFrom = chosen.from;
|
|
124353
|
+
resolvedTo = chosen.to;
|
|
124354
|
+
}
|
|
124355
|
+
}
|
|
124356
|
+
const normalizedFrom = resolveInlineTextPosition(doc2, resolvedFrom, "forward");
|
|
124357
|
+
const normalizedTo = resolveInlineTextPosition(doc2, resolvedTo, "backward");
|
|
124358
|
+
if (Number.isFinite(normalizedFrom) && Number.isFinite(normalizedTo) && normalizedFrom <= normalizedTo) {
|
|
124359
|
+
resolvedFrom = normalizedFrom;
|
|
124360
|
+
resolvedTo = normalizedTo;
|
|
124361
|
+
}
|
|
124362
|
+
return { from: resolvedFrom, to: resolvedTo };
|
|
124363
|
+
};
|
|
124364
|
+
const getPositionTracker = (editor) => {
|
|
124365
|
+
if (!editor) return null;
|
|
124366
|
+
if (editor.positionTracker) return editor.positionTracker;
|
|
124367
|
+
const storageTracker = editor.storage?.positionTracker?.tracker;
|
|
124368
|
+
if (storageTracker) {
|
|
124369
|
+
editor.positionTracker = storageTracker;
|
|
124370
|
+
return storageTracker;
|
|
124371
|
+
}
|
|
124372
|
+
const tracker = new PositionTracker(editor);
|
|
124373
|
+
if (editor.storage?.positionTracker) {
|
|
124374
|
+
editor.storage.positionTracker.tracker = tracker;
|
|
124375
|
+
}
|
|
124376
|
+
editor.positionTracker = tracker;
|
|
124377
|
+
return tracker;
|
|
124378
|
+
};
|
|
123812
124379
|
const Search = Extension.create({
|
|
123813
124380
|
// @ts-expect-error - Storage type mismatch will be fixed in TS migration
|
|
123814
124381
|
addStorage() {
|
|
@@ -123817,29 +124384,58 @@ ${o}
|
|
|
123817
124384
|
* @private
|
|
123818
124385
|
* @type {SearchMatch[]|null}
|
|
123819
124386
|
*/
|
|
123820
|
-
searchResults: []
|
|
124387
|
+
searchResults: [],
|
|
124388
|
+
/**
|
|
124389
|
+
* @private
|
|
124390
|
+
* @type {boolean}
|
|
124391
|
+
* Whether to apply CSS highlight classes to matches
|
|
124392
|
+
*/
|
|
124393
|
+
highlightEnabled: true,
|
|
124394
|
+
/**
|
|
124395
|
+
* @private
|
|
124396
|
+
* @type {SearchIndex}
|
|
124397
|
+
* Lazily-built search index for cross-paragraph matching
|
|
124398
|
+
*/
|
|
124399
|
+
searchIndex: new SearchIndex()
|
|
123821
124400
|
};
|
|
123822
124401
|
},
|
|
123823
124402
|
addPmPlugins() {
|
|
123824
124403
|
const editor = this.editor;
|
|
123825
124404
|
const storage = this.storage;
|
|
124405
|
+
const searchIndexInvalidatorPlugin = new Plugin({
|
|
124406
|
+
key: new PluginKey("searchIndexInvalidator"),
|
|
124407
|
+
appendTransaction(transactions, oldState, newState) {
|
|
124408
|
+
const docChanged = transactions.some((tr) => tr.docChanged);
|
|
124409
|
+
if (docChanged && storage?.searchIndex) {
|
|
124410
|
+
storage.searchIndex.invalidate();
|
|
124411
|
+
}
|
|
124412
|
+
return null;
|
|
124413
|
+
}
|
|
124414
|
+
});
|
|
123826
124415
|
const searchHighlightWithIdPlugin = new Plugin({
|
|
123827
|
-
key:
|
|
124416
|
+
key: customSearchHighlightsKey,
|
|
123828
124417
|
props: {
|
|
123829
124418
|
decorations(state) {
|
|
123830
124419
|
if (!editor) return null;
|
|
123831
124420
|
const matches2 = storage?.searchResults;
|
|
123832
124421
|
if (!matches2?.length) return null;
|
|
123833
|
-
const
|
|
123834
|
-
|
|
123835
|
-
|
|
123836
|
-
}
|
|
123837
|
-
|
|
124422
|
+
const highlightEnabled = storage?.highlightEnabled !== false;
|
|
124423
|
+
const decorations = [];
|
|
124424
|
+
for (const match of matches2) {
|
|
124425
|
+
const attrs = highlightEnabled ? { id: `search-match-${match.id}`, class: "ProseMirror-search-match" } : { id: `search-match-${match.id}` };
|
|
124426
|
+
if (match.ranges && match.ranges.length > 0) {
|
|
124427
|
+
for (const range2 of match.ranges) {
|
|
124428
|
+
decorations.push(Decoration.inline(range2.from, range2.to, attrs));
|
|
124429
|
+
}
|
|
124430
|
+
} else {
|
|
124431
|
+
decorations.push(Decoration.inline(match.from, match.to, attrs));
|
|
124432
|
+
}
|
|
124433
|
+
}
|
|
123838
124434
|
return DecorationSet.create(state.doc, decorations);
|
|
123839
124435
|
}
|
|
123840
124436
|
}
|
|
123841
124437
|
});
|
|
123842
|
-
return [search$1(), searchHighlightWithIdPlugin];
|
|
124438
|
+
return [search$1(), searchIndexInvalidatorPlugin, searchHighlightWithIdPlugin];
|
|
123843
124439
|
},
|
|
123844
124440
|
addCommands() {
|
|
123845
124441
|
return {
|
|
@@ -123853,21 +124449,51 @@ ${o}
|
|
|
123853
124449
|
goToFirstMatch: () => (
|
|
123854
124450
|
/** @returns {boolean} */
|
|
123855
124451
|
({ state, editor, dispatch }) => {
|
|
124452
|
+
const searchResults = this.storage?.searchResults;
|
|
124453
|
+
if (Array.isArray(searchResults) && searchResults.length > 0) {
|
|
124454
|
+
const firstMatch = searchResults[0];
|
|
124455
|
+
const from2 = firstMatch.ranges?.[0]?.from ?? firstMatch.from;
|
|
124456
|
+
const to = firstMatch.ranges?.[0]?.to ?? firstMatch.to;
|
|
124457
|
+
if (typeof from2 !== "number" || typeof to !== "number") {
|
|
124458
|
+
return false;
|
|
124459
|
+
}
|
|
124460
|
+
editor.view.focus();
|
|
124461
|
+
const tr2 = state.tr.setSelection(TextSelection$1.create(state.doc, from2, to)).scrollIntoView();
|
|
124462
|
+
if (dispatch) dispatch(tr2);
|
|
124463
|
+
const presentationEditor2 = editor.presentationEditor;
|
|
124464
|
+
if (presentationEditor2 && typeof presentationEditor2.scrollToPosition === "function") {
|
|
124465
|
+
const didScroll = presentationEditor2.scrollToPosition(from2, { block: "center" });
|
|
124466
|
+
if (didScroll) return true;
|
|
124467
|
+
}
|
|
124468
|
+
try {
|
|
124469
|
+
const domPos = editor.view.domAtPos(from2);
|
|
124470
|
+
if (domPos?.node?.scrollIntoView) {
|
|
124471
|
+
domPos.node.scrollIntoView(true);
|
|
124472
|
+
}
|
|
124473
|
+
} catch {
|
|
124474
|
+
}
|
|
124475
|
+
return true;
|
|
124476
|
+
}
|
|
123856
124477
|
const highlights = getMatchHighlights(state);
|
|
123857
124478
|
if (!highlights) return false;
|
|
123858
124479
|
const decorations = highlights.find();
|
|
123859
124480
|
if (!decorations?.length) return false;
|
|
123860
|
-
const
|
|
124481
|
+
const firstDeco = decorations[0];
|
|
123861
124482
|
editor.view.focus();
|
|
123862
|
-
const tr = state.tr.setSelection(TextSelection$1.create(state.doc,
|
|
124483
|
+
const tr = state.tr.setSelection(TextSelection$1.create(state.doc, firstDeco.from, firstDeco.to)).scrollIntoView();
|
|
123863
124484
|
if (dispatch) dispatch(tr);
|
|
123864
124485
|
const presentationEditor = editor.presentationEditor;
|
|
123865
124486
|
if (presentationEditor && typeof presentationEditor.scrollToPosition === "function") {
|
|
123866
|
-
const didScroll = presentationEditor.scrollToPosition(
|
|
124487
|
+
const didScroll = presentationEditor.scrollToPosition(firstDeco.from, { block: "center" });
|
|
123867
124488
|
if (didScroll) return true;
|
|
123868
124489
|
}
|
|
123869
|
-
|
|
123870
|
-
|
|
124490
|
+
try {
|
|
124491
|
+
const domPos = editor.view.domAtPos(firstDeco.from);
|
|
124492
|
+
if (domPos?.node?.scrollIntoView) {
|
|
124493
|
+
domPos.node.scrollIntoView(true);
|
|
124494
|
+
}
|
|
124495
|
+
} catch {
|
|
124496
|
+
}
|
|
123871
124497
|
return true;
|
|
123872
124498
|
}
|
|
123873
124499
|
),
|
|
@@ -123885,53 +124511,57 @@ ${o}
|
|
|
123885
124511
|
*
|
|
123886
124512
|
* // Search without visual highlighting
|
|
123887
124513
|
* const silentMatches = editor.commands.search('test', { highlight: false })
|
|
123888
|
-
*
|
|
124514
|
+
*
|
|
124515
|
+
* // Cross-paragraph search (works by default for plain strings)
|
|
124516
|
+
* const crossParagraphMatches = editor.commands.search('end of paragraph start of next')
|
|
124517
|
+
* @note Returns array of SearchMatch objects with positions and IDs.
|
|
124518
|
+
* Plain string searches are whitespace-flexible and match across paragraphs.
|
|
124519
|
+
* Regex searches match exactly as specified.
|
|
123889
124520
|
*/
|
|
123890
124521
|
search: (patternInput, options = {}) => (
|
|
123891
124522
|
/** @returns {SearchMatch[]} */
|
|
123892
|
-
({ state, dispatch }) => {
|
|
124523
|
+
({ state, dispatch, editor }) => {
|
|
123893
124524
|
if (options != null && (typeof options !== "object" || Array.isArray(options))) {
|
|
123894
124525
|
throw new TypeError("Search options must be an object");
|
|
123895
124526
|
}
|
|
123896
124527
|
const highlight = typeof options?.highlight === "boolean" ? options.highlight : true;
|
|
123897
|
-
|
|
124528
|
+
const maxMatches = typeof options?.maxMatches === "number" ? options.maxMatches : 1e3;
|
|
123898
124529
|
let caseSensitive = false;
|
|
123899
|
-
let
|
|
123900
|
-
const wholeWord = false;
|
|
124530
|
+
let searchPattern = patternInput;
|
|
123901
124531
|
if (isRegExp(patternInput)) {
|
|
123902
|
-
|
|
123903
|
-
|
|
123904
|
-
patternInput
|
|
123905
|
-
);
|
|
123906
|
-
regexp = true;
|
|
123907
|
-
pattern = regexPattern.source;
|
|
123908
|
-
caseSensitive = !regexPattern.flags.includes("i");
|
|
124532
|
+
caseSensitive = !patternInput.flags.includes("i");
|
|
124533
|
+
searchPattern = patternInput;
|
|
123909
124534
|
} else if (typeof patternInput === "string" && /^\/(.+)\/([gimsuy]*)$/.test(patternInput)) {
|
|
123910
124535
|
const [, body, flags] = patternInput.match(/^\/(.+)\/([gimsuy]*)$/);
|
|
123911
|
-
regexp = true;
|
|
123912
|
-
pattern = body;
|
|
123913
124536
|
caseSensitive = !flags.includes("i");
|
|
124537
|
+
searchPattern = new RegExp(body, flags.includes("g") ? flags : flags + "g");
|
|
123914
124538
|
} else {
|
|
123915
|
-
|
|
124539
|
+
searchPattern = String(patternInput);
|
|
123916
124540
|
}
|
|
123917
|
-
const
|
|
123918
|
-
|
|
124541
|
+
const searchIndex = this.storage.searchIndex;
|
|
124542
|
+
searchIndex.ensureValid(state.doc);
|
|
124543
|
+
const indexMatches = searchIndex.search(searchPattern, {
|
|
123919
124544
|
caseSensitive,
|
|
123920
|
-
|
|
123921
|
-
wholeWord
|
|
124545
|
+
maxMatches
|
|
123922
124546
|
});
|
|
123923
|
-
const
|
|
123924
|
-
|
|
123925
|
-
|
|
123926
|
-
|
|
123927
|
-
|
|
123928
|
-
|
|
123929
|
-
|
|
123930
|
-
|
|
123931
|
-
|
|
123932
|
-
|
|
123933
|
-
|
|
124547
|
+
const resultMatches = [];
|
|
124548
|
+
for (const indexMatch of indexMatches) {
|
|
124549
|
+
const ranges = searchIndex.offsetRangeToDocRanges(indexMatch.start, indexMatch.end);
|
|
124550
|
+
if (ranges.length === 0) continue;
|
|
124551
|
+
const matchTexts = ranges.map((r2) => state.doc.textBetween(r2.from, r2.to));
|
|
124552
|
+
const combinedText = matchTexts.join("");
|
|
124553
|
+
const match = {
|
|
124554
|
+
from: ranges[0].from,
|
|
124555
|
+
to: ranges[ranges.length - 1].to,
|
|
124556
|
+
text: combinedText,
|
|
124557
|
+
id: v4(),
|
|
124558
|
+
ranges,
|
|
124559
|
+
trackerIds: []
|
|
124560
|
+
};
|
|
124561
|
+
resultMatches.push(match);
|
|
124562
|
+
}
|
|
123934
124563
|
this.storage.searchResults = resultMatches;
|
|
124564
|
+
this.storage.highlightEnabled = highlight;
|
|
123935
124565
|
return resultMatches;
|
|
123936
124566
|
}
|
|
123937
124567
|
),
|
|
@@ -123942,12 +124572,48 @@ ${o}
|
|
|
123942
124572
|
* @example
|
|
123943
124573
|
* const searchResults = editor.commands.search('test string')
|
|
123944
124574
|
* editor.commands.goToSearchResult(searchResults[3])
|
|
123945
|
-
* @note Scrolls to match and selects it
|
|
124575
|
+
* @note Scrolls to match and selects it. For multi-range matches (cross-paragraph),
|
|
124576
|
+
* selects the first range and scrolls to it.
|
|
123946
124577
|
*/
|
|
123947
124578
|
goToSearchResult: (match) => (
|
|
123948
124579
|
/** @returns {boolean} */
|
|
123949
124580
|
({ state, dispatch, editor }) => {
|
|
123950
|
-
const
|
|
124581
|
+
const positionTracker = getPositionTracker(editor);
|
|
124582
|
+
const doc2 = state.doc;
|
|
124583
|
+
const highlights = getMatchHighlights(state);
|
|
124584
|
+
let from2, to;
|
|
124585
|
+
if (match?.ranges && match.ranges.length > 0 && match?.trackerIds && match.trackerIds.length > 0) {
|
|
124586
|
+
if (positionTracker?.resolve && match.trackerIds[0]) {
|
|
124587
|
+
const resolved = positionTracker.resolve(match.trackerIds[0]);
|
|
124588
|
+
if (resolved) {
|
|
124589
|
+
from2 = resolved.from;
|
|
124590
|
+
to = resolved.to;
|
|
124591
|
+
}
|
|
124592
|
+
}
|
|
124593
|
+
if (from2 === void 0) {
|
|
124594
|
+
from2 = match.ranges[0].from;
|
|
124595
|
+
to = match.ranges[0].to;
|
|
124596
|
+
}
|
|
124597
|
+
} else {
|
|
124598
|
+
from2 = match.from;
|
|
124599
|
+
to = match.to;
|
|
124600
|
+
if (positionTracker?.resolve && match?.id) {
|
|
124601
|
+
const resolved = positionTracker.resolve(match.id);
|
|
124602
|
+
if (resolved) {
|
|
124603
|
+
from2 = resolved.from;
|
|
124604
|
+
to = resolved.to;
|
|
124605
|
+
}
|
|
124606
|
+
}
|
|
124607
|
+
}
|
|
124608
|
+
const normalized = resolveSearchRange({
|
|
124609
|
+
doc: doc2,
|
|
124610
|
+
from: from2,
|
|
124611
|
+
to,
|
|
124612
|
+
expectedText: match?.text ?? null,
|
|
124613
|
+
highlights
|
|
124614
|
+
});
|
|
124615
|
+
from2 = normalized.from;
|
|
124616
|
+
to = normalized.to;
|
|
123951
124617
|
editor.view.focus();
|
|
123952
124618
|
const tr = state.tr.setSelection(TextSelection$1.create(state.doc, from2, to)).scrollIntoView();
|
|
123953
124619
|
if (dispatch) dispatch(tr);
|
|
@@ -149687,7 +150353,7 @@ ${reason}`);
|
|
|
149687
150353
|
this.config.colors = shuffleArray(this.config.colors);
|
|
149688
150354
|
this.userColorMap = /* @__PURE__ */ new Map();
|
|
149689
150355
|
this.colorIndex = 0;
|
|
149690
|
-
this.version = "1.6.0-next.
|
|
150356
|
+
this.version = "1.6.0-next.3";
|
|
149691
150357
|
this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
|
|
149692
150358
|
this.superdocId = config2.superdocId || v4();
|
|
149693
150359
|
this.colors = this.config.colors;
|