@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
|
@@ -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-B4dnd1SO.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
|
/**
|
|
@@ -13064,6 +13244,90 @@ const CommentsPlugin = Extension.create({
|
|
|
13064
13244
|
name: "comments",
|
|
13065
13245
|
addCommands() {
|
|
13066
13246
|
return {
|
|
13247
|
+
/**
|
|
13248
|
+
* Add a comment to the current selection
|
|
13249
|
+
* @category Command
|
|
13250
|
+
* @param {string|Object} contentOrOptions - Comment content as a string, or an options object
|
|
13251
|
+
* @param {string} [contentOrOptions.content] - The comment content (text or HTML)
|
|
13252
|
+
* @param {string} [contentOrOptions.author] - Author name (defaults to user from editor config)
|
|
13253
|
+
* @param {string} [contentOrOptions.authorEmail] - Author email (defaults to user from editor config)
|
|
13254
|
+
* @param {string} [contentOrOptions.authorImage] - Author image URL (defaults to user from editor config)
|
|
13255
|
+
* @param {boolean} [contentOrOptions.isInternal=false] - Whether the comment is internal/private
|
|
13256
|
+
* @returns {boolean} True if the comment was added successfully, false otherwise
|
|
13257
|
+
* @example
|
|
13258
|
+
* // Simple usage with just content
|
|
13259
|
+
* editor.commands.addComment('This needs review')
|
|
13260
|
+
*
|
|
13261
|
+
* // With options
|
|
13262
|
+
* editor.commands.addComment({
|
|
13263
|
+
* content: 'Please clarify this section',
|
|
13264
|
+
* author: 'Jane Doe',
|
|
13265
|
+
* isInternal: true
|
|
13266
|
+
* })
|
|
13267
|
+
*
|
|
13268
|
+
* // To get the comment ID, listen to the commentsUpdate event
|
|
13269
|
+
* editor.on('commentsUpdate', (event) => {
|
|
13270
|
+
* if (event.type === 'add') {
|
|
13271
|
+
* console.log('New comment ID:', event.activeCommentId)
|
|
13272
|
+
* }
|
|
13273
|
+
* })
|
|
13274
|
+
*/
|
|
13275
|
+
addComment: (contentOrOptions) => ({ tr, dispatch, editor }) => {
|
|
13276
|
+
const { selection } = tr;
|
|
13277
|
+
const { $from, $to } = selection;
|
|
13278
|
+
if ($from.pos === $to.pos) {
|
|
13279
|
+
console.warn("addComment requires a text selection. Please select text before adding a comment.");
|
|
13280
|
+
return false;
|
|
13281
|
+
}
|
|
13282
|
+
let content, author, authorEmail, authorImage, isInternal;
|
|
13283
|
+
if (typeof contentOrOptions === "string") {
|
|
13284
|
+
content = contentOrOptions;
|
|
13285
|
+
} else if (contentOrOptions && typeof contentOrOptions === "object") {
|
|
13286
|
+
content = contentOrOptions.content;
|
|
13287
|
+
author = contentOrOptions.author;
|
|
13288
|
+
authorEmail = contentOrOptions.authorEmail;
|
|
13289
|
+
authorImage = contentOrOptions.authorImage;
|
|
13290
|
+
isInternal = contentOrOptions.isInternal;
|
|
13291
|
+
}
|
|
13292
|
+
const commentId = v4();
|
|
13293
|
+
const resolvedInternal = isInternal ?? false;
|
|
13294
|
+
const configUser = editor.options?.user || {};
|
|
13295
|
+
tr.setMeta(CommentsPluginKey, { event: "add" });
|
|
13296
|
+
tr.addMark(
|
|
13297
|
+
$from.pos,
|
|
13298
|
+
$to.pos,
|
|
13299
|
+
editor.schema.marks[CommentMarkName$1].create({
|
|
13300
|
+
commentId,
|
|
13301
|
+
internal: resolvedInternal
|
|
13302
|
+
})
|
|
13303
|
+
);
|
|
13304
|
+
if (dispatch) dispatch(tr);
|
|
13305
|
+
const commentPayload = normalizeCommentEventPayload({
|
|
13306
|
+
conversation: {
|
|
13307
|
+
commentId,
|
|
13308
|
+
isInternal: resolvedInternal,
|
|
13309
|
+
commentText: content,
|
|
13310
|
+
creatorName: author ?? configUser.name,
|
|
13311
|
+
creatorEmail: authorEmail ?? configUser.email,
|
|
13312
|
+
creatorImage: authorImage ?? configUser.image,
|
|
13313
|
+
createdTime: Date.now()
|
|
13314
|
+
},
|
|
13315
|
+
editorOptions: editor.options,
|
|
13316
|
+
fallbackCommentId: commentId,
|
|
13317
|
+
fallbackInternal: resolvedInternal
|
|
13318
|
+
});
|
|
13319
|
+
editor.emit("commentsUpdate", {
|
|
13320
|
+
type: comments_module_events.ADD,
|
|
13321
|
+
comment: commentPayload,
|
|
13322
|
+
activeCommentId: commentId
|
|
13323
|
+
});
|
|
13324
|
+
return true;
|
|
13325
|
+
},
|
|
13326
|
+
/**
|
|
13327
|
+
* @private
|
|
13328
|
+
* Internal command to insert a comment mark at the current selection.
|
|
13329
|
+
* Use `addComment` for the public API.
|
|
13330
|
+
*/
|
|
13067
13331
|
insertComment: (conversation = {}) => ({ tr, dispatch }) => {
|
|
13068
13332
|
const { selection } = tr;
|
|
13069
13333
|
const { $from, $to } = selection;
|
|
@@ -15507,7 +15771,7 @@ const canUseDOM = () => {
|
|
|
15507
15771
|
return false;
|
|
15508
15772
|
}
|
|
15509
15773
|
};
|
|
15510
|
-
const summaryVersion = "1.6.0-next.
|
|
15774
|
+
const summaryVersion = "1.6.0-next.3";
|
|
15511
15775
|
const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
|
|
15512
15776
|
const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
|
|
15513
15777
|
function mapAttributes(attrs) {
|
|
@@ -17000,7 +17264,7 @@ class Editor extends EventEmitter {
|
|
|
17000
17264
|
*/
|
|
17001
17265
|
#createExtensionService() {
|
|
17002
17266
|
const allowedExtensions = ["extension", "node", "mark"];
|
|
17003
|
-
const coreExtensions = [Editable, Commands, EditorFocus, Keymap];
|
|
17267
|
+
const coreExtensions = [Editable, Commands, EditorFocus, Keymap, PositionTrackerExtension];
|
|
17004
17268
|
const externalExtensions = this.options.externalExtensions || [];
|
|
17005
17269
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter((extension) => {
|
|
17006
17270
|
const extensionType = typeof extension?.type === "string" ? extension.type : void 0;
|
|
@@ -18174,7 +18438,7 @@ class Editor extends EventEmitter {
|
|
|
18174
18438
|
* Process collaboration migrations
|
|
18175
18439
|
*/
|
|
18176
18440
|
processCollaborationMigrations() {
|
|
18177
|
-
console.debug("[checkVersionMigrations] Current editor version", "1.6.0-next.
|
|
18441
|
+
console.debug("[checkVersionMigrations] Current editor version", "1.6.0-next.3");
|
|
18178
18442
|
if (!this.options.ydoc) return;
|
|
18179
18443
|
const metaMap = this.options.ydoc.getMap("meta");
|
|
18180
18444
|
let docVersion = metaMap.get("version");
|
|
@@ -75527,14 +75791,317 @@ function getMatchHighlights(state) {
|
|
|
75527
75791
|
let search2 = searchKey.getState(state);
|
|
75528
75792
|
return search2 ? search2.deco : DecorationSet.empty;
|
|
75529
75793
|
}
|
|
75530
|
-
|
|
75531
|
-
|
|
75532
|
-
|
|
75794
|
+
const BLOCK_SEPARATOR = "\n";
|
|
75795
|
+
const ATOM_PLACEHOLDER = "";
|
|
75796
|
+
class SearchIndex {
|
|
75797
|
+
/** @type {string} */
|
|
75798
|
+
text = "";
|
|
75799
|
+
/** @type {Segment[]} */
|
|
75800
|
+
segments = [];
|
|
75801
|
+
/** @type {boolean} */
|
|
75802
|
+
valid = false;
|
|
75803
|
+
/** @type {number} */
|
|
75804
|
+
docSize = 0;
|
|
75805
|
+
/**
|
|
75806
|
+
* Build the search index from a ProseMirror document.
|
|
75807
|
+
* Uses doc.textBetween for the flattened string and walks
|
|
75808
|
+
* the document to build the segment offset map.
|
|
75809
|
+
*
|
|
75810
|
+
* @param {import('prosemirror-model').Node} doc - The ProseMirror document
|
|
75811
|
+
*/
|
|
75812
|
+
build(doc2) {
|
|
75813
|
+
this.text = doc2.textBetween(0, doc2.content.size, BLOCK_SEPARATOR, ATOM_PLACEHOLDER);
|
|
75814
|
+
this.segments = [];
|
|
75815
|
+
this.docSize = doc2.content.size;
|
|
75816
|
+
let offset2 = 0;
|
|
75817
|
+
this.#walkNodeContent(doc2, 0, offset2, (segment) => {
|
|
75818
|
+
this.segments.push(segment);
|
|
75819
|
+
offset2 = segment.offsetEnd;
|
|
75820
|
+
});
|
|
75821
|
+
this.valid = true;
|
|
75822
|
+
}
|
|
75823
|
+
/**
|
|
75824
|
+
* Walk the content of a node to build segments.
|
|
75825
|
+
* This method processes the children of a node, given the position
|
|
75826
|
+
* where the node's content starts.
|
|
75827
|
+
*
|
|
75828
|
+
* @param {import('prosemirror-model').Node} node - Current node
|
|
75829
|
+
* @param {number} contentStart - Document position where this node's content starts
|
|
75830
|
+
* @param {number} offset - Current offset in flattened string
|
|
75831
|
+
* @param {(segment: Segment) => void} addSegment - Callback to add a segment
|
|
75832
|
+
* @returns {number} The new offset after processing this node's content
|
|
75833
|
+
*/
|
|
75834
|
+
#walkNodeContent(node, contentStart, offset2, addSegment) {
|
|
75835
|
+
let currentOffset = offset2;
|
|
75836
|
+
let isFirstChild = true;
|
|
75837
|
+
node.forEach((child, childContentOffset) => {
|
|
75838
|
+
const childDocPos = contentStart + childContentOffset;
|
|
75839
|
+
if (child.isBlock && !isFirstChild) {
|
|
75840
|
+
addSegment({
|
|
75841
|
+
offsetStart: currentOffset,
|
|
75842
|
+
offsetEnd: currentOffset + 1,
|
|
75843
|
+
docFrom: childDocPos,
|
|
75844
|
+
docTo: childDocPos,
|
|
75845
|
+
kind: "blockSep"
|
|
75846
|
+
});
|
|
75847
|
+
currentOffset += 1;
|
|
75848
|
+
}
|
|
75849
|
+
currentOffset = this.#walkNode(child, childDocPos, currentOffset, addSegment);
|
|
75850
|
+
isFirstChild = false;
|
|
75851
|
+
});
|
|
75852
|
+
return currentOffset;
|
|
75853
|
+
}
|
|
75854
|
+
/**
|
|
75855
|
+
* Recursively walk a node and its descendants to build segments.
|
|
75856
|
+
*
|
|
75857
|
+
* @param {import('prosemirror-model').Node} node - Current node
|
|
75858
|
+
* @param {number} docPos - Document position at start of this node
|
|
75859
|
+
* @param {number} offset - Current offset in flattened string
|
|
75860
|
+
* @param {(segment: Segment) => void} addSegment - Callback to add a segment
|
|
75861
|
+
* @returns {number} The new offset after processing this node
|
|
75862
|
+
*/
|
|
75863
|
+
#walkNode(node, docPos, offset2, addSegment) {
|
|
75864
|
+
if (node.isText) {
|
|
75865
|
+
const text = node.text || "";
|
|
75866
|
+
if (text.length > 0) {
|
|
75867
|
+
addSegment({
|
|
75868
|
+
offsetStart: offset2,
|
|
75869
|
+
offsetEnd: offset2 + text.length,
|
|
75870
|
+
docFrom: docPos,
|
|
75871
|
+
docTo: docPos + text.length,
|
|
75872
|
+
kind: "text"
|
|
75873
|
+
});
|
|
75874
|
+
return offset2 + text.length;
|
|
75875
|
+
}
|
|
75876
|
+
return offset2;
|
|
75877
|
+
}
|
|
75878
|
+
if (node.isLeaf) {
|
|
75879
|
+
if (node.type.name === "hard_break") {
|
|
75880
|
+
addSegment({
|
|
75881
|
+
offsetStart: offset2,
|
|
75882
|
+
offsetEnd: offset2 + 1,
|
|
75883
|
+
docFrom: docPos,
|
|
75884
|
+
docTo: docPos + node.nodeSize,
|
|
75885
|
+
kind: "hardBreak"
|
|
75886
|
+
});
|
|
75887
|
+
return offset2 + 1;
|
|
75888
|
+
}
|
|
75889
|
+
addSegment({
|
|
75890
|
+
offsetStart: offset2,
|
|
75891
|
+
offsetEnd: offset2 + 1,
|
|
75892
|
+
docFrom: docPos,
|
|
75893
|
+
docTo: docPos + node.nodeSize,
|
|
75894
|
+
kind: "atom"
|
|
75895
|
+
});
|
|
75896
|
+
return offset2 + 1;
|
|
75897
|
+
}
|
|
75898
|
+
return this.#walkNodeContent(node, docPos + 1, offset2, addSegment);
|
|
75899
|
+
}
|
|
75900
|
+
/**
|
|
75901
|
+
* Mark the index as stale. It will be rebuilt on next search.
|
|
75902
|
+
*/
|
|
75903
|
+
invalidate() {
|
|
75904
|
+
this.valid = false;
|
|
75905
|
+
}
|
|
75906
|
+
/**
|
|
75907
|
+
* Check if the index needs rebuilding for the given document.
|
|
75908
|
+
*
|
|
75909
|
+
* @param {import('prosemirror-model').Node} doc - The document to check against
|
|
75910
|
+
* @returns {boolean} True if index is stale and needs rebuilding
|
|
75911
|
+
*/
|
|
75912
|
+
isStale(doc2) {
|
|
75913
|
+
return !this.valid || doc2.content.size !== this.docSize;
|
|
75914
|
+
}
|
|
75915
|
+
/**
|
|
75916
|
+
* Ensure the index is valid for the given document.
|
|
75917
|
+
* Rebuilds if stale.
|
|
75918
|
+
*
|
|
75919
|
+
* @param {import('prosemirror-model').Node} doc - The document
|
|
75920
|
+
*/
|
|
75921
|
+
ensureValid(doc2) {
|
|
75922
|
+
if (this.isStale(doc2)) {
|
|
75923
|
+
this.build(doc2);
|
|
75924
|
+
}
|
|
75925
|
+
}
|
|
75926
|
+
/**
|
|
75927
|
+
* Convert an offset range in the flattened string to document ranges.
|
|
75928
|
+
* Skips separator/atom segments and returns only text ranges.
|
|
75929
|
+
*
|
|
75930
|
+
* @param {number} start - Start offset in flattened string
|
|
75931
|
+
* @param {number} end - End offset in flattened string
|
|
75932
|
+
* @returns {DocRange[]} Array of document ranges (text segments only)
|
|
75933
|
+
*/
|
|
75934
|
+
offsetRangeToDocRanges(start2, end2) {
|
|
75935
|
+
const ranges = [];
|
|
75936
|
+
for (const segment of this.segments) {
|
|
75937
|
+
if (segment.offsetEnd <= start2) continue;
|
|
75938
|
+
if (segment.offsetStart >= end2) break;
|
|
75939
|
+
if (segment.kind !== "text") continue;
|
|
75940
|
+
const overlapStart = Math.max(start2, segment.offsetStart);
|
|
75941
|
+
const overlapEnd = Math.min(end2, segment.offsetEnd);
|
|
75942
|
+
if (overlapStart < overlapEnd) {
|
|
75943
|
+
const startInSegment = overlapStart - segment.offsetStart;
|
|
75944
|
+
const endInSegment = overlapEnd - segment.offsetStart;
|
|
75945
|
+
ranges.push({
|
|
75946
|
+
from: segment.docFrom + startInSegment,
|
|
75947
|
+
to: segment.docFrom + endInSegment
|
|
75948
|
+
});
|
|
75949
|
+
}
|
|
75950
|
+
}
|
|
75951
|
+
return ranges;
|
|
75952
|
+
}
|
|
75953
|
+
/**
|
|
75954
|
+
* Find the document position for a given offset in the flattened string.
|
|
75955
|
+
*
|
|
75956
|
+
* @param {number} offset - Offset in flattened string
|
|
75957
|
+
* @returns {number|null} Document position, or null if not found
|
|
75958
|
+
*/
|
|
75959
|
+
offsetToDocPos(offset2) {
|
|
75960
|
+
for (const segment of this.segments) {
|
|
75961
|
+
if (offset2 >= segment.offsetStart && offset2 < segment.offsetEnd) {
|
|
75962
|
+
if (segment.kind === "text") {
|
|
75963
|
+
return segment.docFrom + (offset2 - segment.offsetStart);
|
|
75964
|
+
}
|
|
75965
|
+
return segment.docFrom;
|
|
75966
|
+
}
|
|
75967
|
+
}
|
|
75968
|
+
if (this.segments.length > 0 && offset2 === this.segments[this.segments.length - 1].offsetEnd) {
|
|
75969
|
+
const lastSeg = this.segments[this.segments.length - 1];
|
|
75970
|
+
return lastSeg.docTo;
|
|
75971
|
+
}
|
|
75972
|
+
return null;
|
|
75973
|
+
}
|
|
75974
|
+
/**
|
|
75975
|
+
* Escape special regex characters in a string.
|
|
75976
|
+
*
|
|
75977
|
+
* @param {string} str - String to escape
|
|
75978
|
+
* @returns {string} Escaped string safe for use in RegExp
|
|
75979
|
+
*/
|
|
75980
|
+
static escapeRegex(str) {
|
|
75981
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
75982
|
+
}
|
|
75983
|
+
/**
|
|
75984
|
+
* Convert a plain search string to a whitespace-flexible regex pattern.
|
|
75985
|
+
* This allows matching across paragraph boundaries.
|
|
75986
|
+
*
|
|
75987
|
+
* @param {string} searchString - The search string
|
|
75988
|
+
* @returns {string} Regex pattern string
|
|
75989
|
+
*/
|
|
75990
|
+
static toFlexiblePattern(searchString) {
|
|
75991
|
+
const parts = searchString.split(/\s+/).filter((part) => part.length > 0);
|
|
75992
|
+
if (parts.length === 0) return "";
|
|
75993
|
+
return parts.map((part) => SearchIndex.escapeRegex(part)).join("\\s+");
|
|
75994
|
+
}
|
|
75995
|
+
/**
|
|
75996
|
+
* Search the index for matches.
|
|
75997
|
+
*
|
|
75998
|
+
* @param {string | RegExp} pattern - Search pattern (string or regex)
|
|
75999
|
+
* @param {Object} options - Search options
|
|
76000
|
+
* @param {boolean} [options.caseSensitive=false] - Case sensitive search
|
|
76001
|
+
* @param {number} [options.maxMatches=1000] - Maximum number of matches to return
|
|
76002
|
+
* @returns {Array<{start: number, end: number, text: string}>} Array of matches with offsets
|
|
76003
|
+
*/
|
|
76004
|
+
search(pattern, options = {}) {
|
|
76005
|
+
const { caseSensitive = false, maxMatches = 1e3 } = options;
|
|
76006
|
+
const matches = [];
|
|
76007
|
+
let regex;
|
|
76008
|
+
if (pattern instanceof RegExp) {
|
|
76009
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
|
|
76010
|
+
regex = new RegExp(pattern.source, flags);
|
|
76011
|
+
} else if (typeof pattern === "string") {
|
|
76012
|
+
if (pattern.length === 0) return matches;
|
|
76013
|
+
const flexiblePattern = SearchIndex.toFlexiblePattern(pattern);
|
|
76014
|
+
if (flexiblePattern.length === 0) return matches;
|
|
76015
|
+
const flags = caseSensitive ? "g" : "gi";
|
|
76016
|
+
regex = new RegExp(flexiblePattern, flags);
|
|
76017
|
+
} else {
|
|
76018
|
+
return matches;
|
|
76019
|
+
}
|
|
76020
|
+
let match;
|
|
76021
|
+
while ((match = regex.exec(this.text)) !== null && matches.length < maxMatches) {
|
|
76022
|
+
matches.push({
|
|
76023
|
+
start: match.index,
|
|
76024
|
+
end: match.index + match[0].length,
|
|
76025
|
+
text: match[0]
|
|
76026
|
+
});
|
|
76027
|
+
if (match[0].length === 0) {
|
|
76028
|
+
regex.lastIndex++;
|
|
76029
|
+
}
|
|
76030
|
+
}
|
|
76031
|
+
return matches;
|
|
75533
76032
|
}
|
|
75534
|
-
const highlight = typeof options?.highlight === "boolean" ? options.highlight : true;
|
|
75535
|
-
return tr.setMeta(searchKey, { query, range, highlight });
|
|
75536
76033
|
}
|
|
76034
|
+
const customSearchHighlightsKey = new PluginKey("customSearchHighlights");
|
|
75537
76035
|
const isRegExp = (value) => Object.prototype.toString.call(value) === "[object RegExp]";
|
|
76036
|
+
const resolveInlineTextPosition = (doc2, position, direction) => {
|
|
76037
|
+
const docSize = doc2.content.size;
|
|
76038
|
+
if (!Number.isFinite(position) || position < 0 || position > docSize) {
|
|
76039
|
+
return position;
|
|
76040
|
+
}
|
|
76041
|
+
const step = direction === "forward" ? 1 : -1;
|
|
76042
|
+
let current = position;
|
|
76043
|
+
let iterations = 0;
|
|
76044
|
+
while (iterations < 8) {
|
|
76045
|
+
iterations += 1;
|
|
76046
|
+
const resolved = doc2.resolve(current);
|
|
76047
|
+
const boundaryNode = direction === "forward" ? resolved.nodeAfter : resolved.nodeBefore;
|
|
76048
|
+
if (!boundaryNode) break;
|
|
76049
|
+
if (boundaryNode.isText) break;
|
|
76050
|
+
if (!boundaryNode.isInline || boundaryNode.isAtom || boundaryNode.content.size === 0) break;
|
|
76051
|
+
const next = current + step;
|
|
76052
|
+
if (next < 0 || next > docSize) break;
|
|
76053
|
+
current = next;
|
|
76054
|
+
const adjacent = doc2.resolve(current);
|
|
76055
|
+
const checkNode = direction === "forward" ? adjacent.nodeAfter : adjacent.nodeBefore;
|
|
76056
|
+
if (checkNode && checkNode.isText) break;
|
|
76057
|
+
}
|
|
76058
|
+
return current;
|
|
76059
|
+
};
|
|
76060
|
+
const resolveSearchRange = ({ doc: doc2, from: from3, to, expectedText, highlights }) => {
|
|
76061
|
+
const docSize = doc2.content.size;
|
|
76062
|
+
let resolvedFrom = Math.max(0, Math.min(from3, docSize));
|
|
76063
|
+
let resolvedTo = Math.max(0, Math.min(to, docSize));
|
|
76064
|
+
if (highlights) {
|
|
76065
|
+
const windowStart = Math.max(0, resolvedFrom - 4);
|
|
76066
|
+
const windowEnd = Math.min(docSize, resolvedTo + 4);
|
|
76067
|
+
const candidates = highlights.find(windowStart, windowEnd);
|
|
76068
|
+
if (candidates.length > 0) {
|
|
76069
|
+
let chosen = candidates[0];
|
|
76070
|
+
if (expectedText) {
|
|
76071
|
+
const matching = candidates.filter(
|
|
76072
|
+
(decoration) => doc2.textBetween(decoration.from, decoration.to) === expectedText
|
|
76073
|
+
);
|
|
76074
|
+
if (matching.length > 0) {
|
|
76075
|
+
chosen = matching[0];
|
|
76076
|
+
}
|
|
76077
|
+
}
|
|
76078
|
+
resolvedFrom = chosen.from;
|
|
76079
|
+
resolvedTo = chosen.to;
|
|
76080
|
+
}
|
|
76081
|
+
}
|
|
76082
|
+
const normalizedFrom = resolveInlineTextPosition(doc2, resolvedFrom, "forward");
|
|
76083
|
+
const normalizedTo = resolveInlineTextPosition(doc2, resolvedTo, "backward");
|
|
76084
|
+
if (Number.isFinite(normalizedFrom) && Number.isFinite(normalizedTo) && normalizedFrom <= normalizedTo) {
|
|
76085
|
+
resolvedFrom = normalizedFrom;
|
|
76086
|
+
resolvedTo = normalizedTo;
|
|
76087
|
+
}
|
|
76088
|
+
return { from: resolvedFrom, to: resolvedTo };
|
|
76089
|
+
};
|
|
76090
|
+
const getPositionTracker = (editor) => {
|
|
76091
|
+
if (!editor) return null;
|
|
76092
|
+
if (editor.positionTracker) return editor.positionTracker;
|
|
76093
|
+
const storageTracker = editor.storage?.positionTracker?.tracker;
|
|
76094
|
+
if (storageTracker) {
|
|
76095
|
+
editor.positionTracker = storageTracker;
|
|
76096
|
+
return storageTracker;
|
|
76097
|
+
}
|
|
76098
|
+
const tracker = new PositionTracker(editor);
|
|
76099
|
+
if (editor.storage?.positionTracker) {
|
|
76100
|
+
editor.storage.positionTracker.tracker = tracker;
|
|
76101
|
+
}
|
|
76102
|
+
editor.positionTracker = tracker;
|
|
76103
|
+
return tracker;
|
|
76104
|
+
};
|
|
75538
76105
|
const Search = Extension.create({
|
|
75539
76106
|
// @ts-expect-error - Storage type mismatch will be fixed in TS migration
|
|
75540
76107
|
addStorage() {
|
|
@@ -75543,29 +76110,58 @@ const Search = Extension.create({
|
|
|
75543
76110
|
* @private
|
|
75544
76111
|
* @type {SearchMatch[]|null}
|
|
75545
76112
|
*/
|
|
75546
|
-
searchResults: []
|
|
76113
|
+
searchResults: [],
|
|
76114
|
+
/**
|
|
76115
|
+
* @private
|
|
76116
|
+
* @type {boolean}
|
|
76117
|
+
* Whether to apply CSS highlight classes to matches
|
|
76118
|
+
*/
|
|
76119
|
+
highlightEnabled: true,
|
|
76120
|
+
/**
|
|
76121
|
+
* @private
|
|
76122
|
+
* @type {SearchIndex}
|
|
76123
|
+
* Lazily-built search index for cross-paragraph matching
|
|
76124
|
+
*/
|
|
76125
|
+
searchIndex: new SearchIndex()
|
|
75547
76126
|
};
|
|
75548
76127
|
},
|
|
75549
76128
|
addPmPlugins() {
|
|
75550
76129
|
const editor = this.editor;
|
|
75551
76130
|
const storage = this.storage;
|
|
76131
|
+
const searchIndexInvalidatorPlugin = new Plugin({
|
|
76132
|
+
key: new PluginKey("searchIndexInvalidator"),
|
|
76133
|
+
appendTransaction(transactions, oldState, newState) {
|
|
76134
|
+
const docChanged = transactions.some((tr) => tr.docChanged);
|
|
76135
|
+
if (docChanged && storage?.searchIndex) {
|
|
76136
|
+
storage.searchIndex.invalidate();
|
|
76137
|
+
}
|
|
76138
|
+
return null;
|
|
76139
|
+
}
|
|
76140
|
+
});
|
|
75552
76141
|
const searchHighlightWithIdPlugin = new Plugin({
|
|
75553
|
-
key:
|
|
76142
|
+
key: customSearchHighlightsKey,
|
|
75554
76143
|
props: {
|
|
75555
76144
|
decorations(state) {
|
|
75556
76145
|
if (!editor) return null;
|
|
75557
76146
|
const matches = storage?.searchResults;
|
|
75558
76147
|
if (!matches?.length) return null;
|
|
75559
|
-
const
|
|
75560
|
-
|
|
75561
|
-
|
|
75562
|
-
}
|
|
75563
|
-
|
|
76148
|
+
const highlightEnabled = storage?.highlightEnabled !== false;
|
|
76149
|
+
const decorations = [];
|
|
76150
|
+
for (const match of matches) {
|
|
76151
|
+
const attrs = highlightEnabled ? { id: `search-match-${match.id}`, class: "ProseMirror-search-match" } : { id: `search-match-${match.id}` };
|
|
76152
|
+
if (match.ranges && match.ranges.length > 0) {
|
|
76153
|
+
for (const range of match.ranges) {
|
|
76154
|
+
decorations.push(Decoration.inline(range.from, range.to, attrs));
|
|
76155
|
+
}
|
|
76156
|
+
} else {
|
|
76157
|
+
decorations.push(Decoration.inline(match.from, match.to, attrs));
|
|
76158
|
+
}
|
|
76159
|
+
}
|
|
75564
76160
|
return DecorationSet.create(state.doc, decorations);
|
|
75565
76161
|
}
|
|
75566
76162
|
}
|
|
75567
76163
|
});
|
|
75568
|
-
return [search(), searchHighlightWithIdPlugin];
|
|
76164
|
+
return [search(), searchIndexInvalidatorPlugin, searchHighlightWithIdPlugin];
|
|
75569
76165
|
},
|
|
75570
76166
|
addCommands() {
|
|
75571
76167
|
return {
|
|
@@ -75579,21 +76175,51 @@ const Search = Extension.create({
|
|
|
75579
76175
|
goToFirstMatch: () => (
|
|
75580
76176
|
/** @returns {boolean} */
|
|
75581
76177
|
({ state, editor, dispatch }) => {
|
|
76178
|
+
const searchResults = this.storage?.searchResults;
|
|
76179
|
+
if (Array.isArray(searchResults) && searchResults.length > 0) {
|
|
76180
|
+
const firstMatch = searchResults[0];
|
|
76181
|
+
const from3 = firstMatch.ranges?.[0]?.from ?? firstMatch.from;
|
|
76182
|
+
const to = firstMatch.ranges?.[0]?.to ?? firstMatch.to;
|
|
76183
|
+
if (typeof from3 !== "number" || typeof to !== "number") {
|
|
76184
|
+
return false;
|
|
76185
|
+
}
|
|
76186
|
+
editor.view.focus();
|
|
76187
|
+
const tr2 = state.tr.setSelection(TextSelection$1.create(state.doc, from3, to)).scrollIntoView();
|
|
76188
|
+
if (dispatch) dispatch(tr2);
|
|
76189
|
+
const presentationEditor2 = editor.presentationEditor;
|
|
76190
|
+
if (presentationEditor2 && typeof presentationEditor2.scrollToPosition === "function") {
|
|
76191
|
+
const didScroll = presentationEditor2.scrollToPosition(from3, { block: "center" });
|
|
76192
|
+
if (didScroll) return true;
|
|
76193
|
+
}
|
|
76194
|
+
try {
|
|
76195
|
+
const domPos = editor.view.domAtPos(from3);
|
|
76196
|
+
if (domPos?.node?.scrollIntoView) {
|
|
76197
|
+
domPos.node.scrollIntoView(true);
|
|
76198
|
+
}
|
|
76199
|
+
} catch {
|
|
76200
|
+
}
|
|
76201
|
+
return true;
|
|
76202
|
+
}
|
|
75582
76203
|
const highlights = getMatchHighlights(state);
|
|
75583
76204
|
if (!highlights) return false;
|
|
75584
76205
|
const decorations = highlights.find();
|
|
75585
76206
|
if (!decorations?.length) return false;
|
|
75586
|
-
const
|
|
76207
|
+
const firstDeco = decorations[0];
|
|
75587
76208
|
editor.view.focus();
|
|
75588
|
-
const tr = state.tr.setSelection(TextSelection$1.create(state.doc,
|
|
76209
|
+
const tr = state.tr.setSelection(TextSelection$1.create(state.doc, firstDeco.from, firstDeco.to)).scrollIntoView();
|
|
75589
76210
|
if (dispatch) dispatch(tr);
|
|
75590
76211
|
const presentationEditor = editor.presentationEditor;
|
|
75591
76212
|
if (presentationEditor && typeof presentationEditor.scrollToPosition === "function") {
|
|
75592
|
-
const didScroll = presentationEditor.scrollToPosition(
|
|
76213
|
+
const didScroll = presentationEditor.scrollToPosition(firstDeco.from, { block: "center" });
|
|
75593
76214
|
if (didScroll) return true;
|
|
75594
76215
|
}
|
|
75595
|
-
|
|
75596
|
-
|
|
76216
|
+
try {
|
|
76217
|
+
const domPos = editor.view.domAtPos(firstDeco.from);
|
|
76218
|
+
if (domPos?.node?.scrollIntoView) {
|
|
76219
|
+
domPos.node.scrollIntoView(true);
|
|
76220
|
+
}
|
|
76221
|
+
} catch {
|
|
76222
|
+
}
|
|
75597
76223
|
return true;
|
|
75598
76224
|
}
|
|
75599
76225
|
),
|
|
@@ -75611,53 +76237,57 @@ const Search = Extension.create({
|
|
|
75611
76237
|
*
|
|
75612
76238
|
* // Search without visual highlighting
|
|
75613
76239
|
* const silentMatches = editor.commands.search('test', { highlight: false })
|
|
75614
|
-
*
|
|
76240
|
+
*
|
|
76241
|
+
* // Cross-paragraph search (works by default for plain strings)
|
|
76242
|
+
* const crossParagraphMatches = editor.commands.search('end of paragraph start of next')
|
|
76243
|
+
* @note Returns array of SearchMatch objects with positions and IDs.
|
|
76244
|
+
* Plain string searches are whitespace-flexible and match across paragraphs.
|
|
76245
|
+
* Regex searches match exactly as specified.
|
|
75615
76246
|
*/
|
|
75616
76247
|
search: (patternInput, options = {}) => (
|
|
75617
76248
|
/** @returns {SearchMatch[]} */
|
|
75618
|
-
({ state, dispatch }) => {
|
|
76249
|
+
({ state, dispatch, editor }) => {
|
|
75619
76250
|
if (options != null && (typeof options !== "object" || Array.isArray(options))) {
|
|
75620
76251
|
throw new TypeError("Search options must be an object");
|
|
75621
76252
|
}
|
|
75622
76253
|
const highlight = typeof options?.highlight === "boolean" ? options.highlight : true;
|
|
75623
|
-
|
|
76254
|
+
const maxMatches = typeof options?.maxMatches === "number" ? options.maxMatches : 1e3;
|
|
75624
76255
|
let caseSensitive = false;
|
|
75625
|
-
let
|
|
75626
|
-
const wholeWord = false;
|
|
76256
|
+
let searchPattern = patternInput;
|
|
75627
76257
|
if (isRegExp(patternInput)) {
|
|
75628
|
-
|
|
75629
|
-
|
|
75630
|
-
patternInput
|
|
75631
|
-
);
|
|
75632
|
-
regexp = true;
|
|
75633
|
-
pattern = regexPattern.source;
|
|
75634
|
-
caseSensitive = !regexPattern.flags.includes("i");
|
|
76258
|
+
caseSensitive = !patternInput.flags.includes("i");
|
|
76259
|
+
searchPattern = patternInput;
|
|
75635
76260
|
} else if (typeof patternInput === "string" && /^\/(.+)\/([gimsuy]*)$/.test(patternInput)) {
|
|
75636
76261
|
const [, body, flags] = patternInput.match(/^\/(.+)\/([gimsuy]*)$/);
|
|
75637
|
-
regexp = true;
|
|
75638
|
-
pattern = body;
|
|
75639
76262
|
caseSensitive = !flags.includes("i");
|
|
76263
|
+
searchPattern = new RegExp(body, flags.includes("g") ? flags : flags + "g");
|
|
75640
76264
|
} else {
|
|
75641
|
-
|
|
76265
|
+
searchPattern = String(patternInput);
|
|
75642
76266
|
}
|
|
75643
|
-
const
|
|
75644
|
-
|
|
76267
|
+
const searchIndex = this.storage.searchIndex;
|
|
76268
|
+
searchIndex.ensureValid(state.doc);
|
|
76269
|
+
const indexMatches = searchIndex.search(searchPattern, {
|
|
75645
76270
|
caseSensitive,
|
|
75646
|
-
|
|
75647
|
-
wholeWord
|
|
76271
|
+
maxMatches
|
|
75648
76272
|
});
|
|
75649
|
-
const
|
|
75650
|
-
|
|
75651
|
-
|
|
75652
|
-
|
|
75653
|
-
|
|
75654
|
-
|
|
75655
|
-
|
|
75656
|
-
|
|
75657
|
-
|
|
75658
|
-
|
|
75659
|
-
|
|
76273
|
+
const resultMatches = [];
|
|
76274
|
+
for (const indexMatch of indexMatches) {
|
|
76275
|
+
const ranges = searchIndex.offsetRangeToDocRanges(indexMatch.start, indexMatch.end);
|
|
76276
|
+
if (ranges.length === 0) continue;
|
|
76277
|
+
const matchTexts = ranges.map((r2) => state.doc.textBetween(r2.from, r2.to));
|
|
76278
|
+
const combinedText = matchTexts.join("");
|
|
76279
|
+
const match = {
|
|
76280
|
+
from: ranges[0].from,
|
|
76281
|
+
to: ranges[ranges.length - 1].to,
|
|
76282
|
+
text: combinedText,
|
|
76283
|
+
id: v4(),
|
|
76284
|
+
ranges,
|
|
76285
|
+
trackerIds: []
|
|
76286
|
+
};
|
|
76287
|
+
resultMatches.push(match);
|
|
76288
|
+
}
|
|
75660
76289
|
this.storage.searchResults = resultMatches;
|
|
76290
|
+
this.storage.highlightEnabled = highlight;
|
|
75661
76291
|
return resultMatches;
|
|
75662
76292
|
}
|
|
75663
76293
|
),
|
|
@@ -75668,12 +76298,48 @@ const Search = Extension.create({
|
|
|
75668
76298
|
* @example
|
|
75669
76299
|
* const searchResults = editor.commands.search('test string')
|
|
75670
76300
|
* editor.commands.goToSearchResult(searchResults[3])
|
|
75671
|
-
* @note Scrolls to match and selects it
|
|
76301
|
+
* @note Scrolls to match and selects it. For multi-range matches (cross-paragraph),
|
|
76302
|
+
* selects the first range and scrolls to it.
|
|
75672
76303
|
*/
|
|
75673
76304
|
goToSearchResult: (match) => (
|
|
75674
76305
|
/** @returns {boolean} */
|
|
75675
76306
|
({ state, dispatch, editor }) => {
|
|
75676
|
-
const
|
|
76307
|
+
const positionTracker = getPositionTracker(editor);
|
|
76308
|
+
const doc2 = state.doc;
|
|
76309
|
+
const highlights = getMatchHighlights(state);
|
|
76310
|
+
let from3, to;
|
|
76311
|
+
if (match?.ranges && match.ranges.length > 0 && match?.trackerIds && match.trackerIds.length > 0) {
|
|
76312
|
+
if (positionTracker?.resolve && match.trackerIds[0]) {
|
|
76313
|
+
const resolved = positionTracker.resolve(match.trackerIds[0]);
|
|
76314
|
+
if (resolved) {
|
|
76315
|
+
from3 = resolved.from;
|
|
76316
|
+
to = resolved.to;
|
|
76317
|
+
}
|
|
76318
|
+
}
|
|
76319
|
+
if (from3 === void 0) {
|
|
76320
|
+
from3 = match.ranges[0].from;
|
|
76321
|
+
to = match.ranges[0].to;
|
|
76322
|
+
}
|
|
76323
|
+
} else {
|
|
76324
|
+
from3 = match.from;
|
|
76325
|
+
to = match.to;
|
|
76326
|
+
if (positionTracker?.resolve && match?.id) {
|
|
76327
|
+
const resolved = positionTracker.resolve(match.id);
|
|
76328
|
+
if (resolved) {
|
|
76329
|
+
from3 = resolved.from;
|
|
76330
|
+
to = resolved.to;
|
|
76331
|
+
}
|
|
76332
|
+
}
|
|
76333
|
+
}
|
|
76334
|
+
const normalized = resolveSearchRange({
|
|
76335
|
+
doc: doc2,
|
|
76336
|
+
from: from3,
|
|
76337
|
+
to,
|
|
76338
|
+
expectedText: match?.text ?? null,
|
|
76339
|
+
highlights
|
|
76340
|
+
});
|
|
76341
|
+
from3 = normalized.from;
|
|
76342
|
+
to = normalized.to;
|
|
75677
76343
|
editor.view.focus();
|
|
75678
76344
|
const tr = state.tr.setSelection(TextSelection$1.create(state.doc, from3, to)).scrollIntoView();
|
|
75679
76345
|
if (dispatch) dispatch(tr);
|