@mdxeditor/editor 3.39.0 → 3.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -798,6 +798,8 @@ export declare function curry1to0<T, R>(proc: (arg: T) => R, arg: T): () => R;
798
798
  */
799
799
  export declare function curry2to1<T, K, R>(proc: (arg1: T, arg2: K) => R, arg1: T): (arg2: K) => R;
800
800
 
801
+ export declare const debouncedIndexer$: NodeRef<TextNodeIndex>;
802
+
801
803
  export declare const DEFAULT_FORMAT: 0;
802
804
 
803
805
  /**
@@ -1131,6 +1133,18 @@ export declare interface EditorInFocus {
1131
1133
  */
1132
1134
  export declare const editorRootElementRef$: NodeRef<default_2.RefObject<HTMLDivElement> | null>;
1133
1135
 
1136
+ export declare const editorSearchCursor$: NodeRef<number>;
1137
+
1138
+ export declare const editorSearchRanges$: NodeRef<Range[]>;
1139
+
1140
+ export declare const editorSearchScollableContent$: NodeRef<HTMLElement | null>;
1141
+
1142
+ export declare const editorSearchTerm$: NodeRef<string>;
1143
+
1144
+ export declare const editorSearchTermDebounced$: NodeRef<string>;
1145
+
1146
+ export declare const editorSearchTextNodeIndex$: NodeRef<TextNodeIndex>;
1147
+
1134
1148
  /**
1135
1149
  * A function that subscribes to Lexical editor updates or events, and retursns an unsubscribe function.
1136
1150
  * @group Core
@@ -1139,6 +1153,8 @@ export declare type EditorSubscription = (activeEditor: LexicalEditor) => () =>
1139
1153
 
1140
1154
  /* Excluded from this release type: editorWrappers$ */
1141
1155
 
1156
+ export declare const EmptyTextNodeIndex: TextNodeIndex;
1157
+
1142
1158
  /* Excluded from this release type: ExportLexicalTreeOptions */
1143
1159
 
1144
1160
  /* Excluded from this release type: exportLexicalTreeToMdast */
@@ -2134,6 +2150,10 @@ export declare type MdastJsx = MdxJsxTextElement | MdxJsxFlowElement;
2134
2150
 
2135
2151
  /* Excluded from this release type: MdastTreeImportOptions */
2136
2152
 
2153
+ export declare const MDX_FOCUS_SEARCH_NAME = "MdxFocusSearch";
2154
+
2155
+ export declare const MDX_SEARCH_NAME = "MdxSearch";
2156
+
2137
2157
  /**
2138
2158
  * The MDXEditor React component.
2139
2159
  * @group MDXEditor
@@ -2472,6 +2492,8 @@ export declare const PropertyPopover: default_2.FC<{
2472
2492
  */
2473
2493
  export declare const quotePlugin: (params?: unknown) => RealmPlugin;
2474
2494
 
2495
+ export declare function rangeSearchScan(searchQuery: string, { allText, offsetIndex, nodeIndex }: TextNodeIndex): Generator<Range, void, unknown>;
2496
+
2475
2497
  /**
2476
2498
  * Holds the readOnly state of the editor.
2477
2499
  * @group Core
@@ -2660,6 +2682,10 @@ export declare interface SaveImageParameters extends BaseImageParameters {
2660
2682
  file?: FileList;
2661
2683
  }
2662
2684
 
2685
+ export declare const searchOpen$: NodeRef<boolean>;
2686
+
2687
+ export declare const searchPlugin: (params?: unknown) => RealmPlugin;
2688
+
2663
2689
  /**
2664
2690
  * A toolbar primitive you can use to build dropdowns, such as the block type select.
2665
2691
  * @group Toolbar Primitives
@@ -2879,6 +2905,12 @@ export declare const tablePlugin: (params?: Options_3 | undefined) => RealmPlugi
2879
2905
  */
2880
2906
  export declare function tap<T>(arg: T, proc: (arg: T) => unknown): T;
2881
2907
 
2908
+ export declare interface TextNodeIndex {
2909
+ allText: string;
2910
+ nodeIndex: Node[];
2911
+ offsetIndex: number[];
2912
+ }
2913
+
2882
2914
  /**
2883
2915
  * A plugin that adds support for thematic breaks.
2884
2916
  * @group Thematic Break
@@ -2981,6 +3013,28 @@ export declare function useCodeBlockEditorContext(): CodeBlockEditorContextValue
2981
3013
  */
2982
3014
  export declare const usedLexicalNodes$: NodeRef< KlassConstructor<typeof LexicalNode>[]>;
2983
3015
 
3016
+ export declare function useEditorSearch(): {
3017
+ next: () => void;
3018
+ prev: () => void;
3019
+ total: number;
3020
+ cursor: number;
3021
+ setSearch: (term: string | null) => void;
3022
+ search: string;
3023
+ currentRange: Range;
3024
+ isSearchOpen: boolean;
3025
+ setIsSearchOpen: (value: boolean) => void;
3026
+ openSearch: () => void;
3027
+ closeSearch: () => void;
3028
+ toggleSearch: () => void;
3029
+ ranges: Range[];
3030
+ scrollToRangeOrIndex: (range: Range | number, options?: {
3031
+ ignoreIfInView?: boolean;
3032
+ behavior?: ScrollBehavior;
3033
+ }) => void;
3034
+ replace: (str: string, onUpdate?: () => void) => void;
3035
+ replaceAll: (str: string, onUpdate?: () => void) => void;
3036
+ };
3037
+
2984
3038
  /**
2985
3039
  * A hook that returns a function that removes the lexical node from the editor.
2986
3040
  * @group Custom Editor Primitives
package/dist/index.js CHANGED
@@ -28,6 +28,7 @@ import { applyLinkChanges$, cancelLinkEdit$, linkAutocompleteSuggestions$, linkD
28
28
  import { toolbarClassName$, toolbarContents$, toolbarPlugin } from "./plugins/toolbar/index.js";
29
29
  import { cmExtensions$, diffMarkdown$, diffSourcePlugin, readOnlyDiff$ } from "./plugins/diff-source/index.js";
30
30
  import { markdownShortcutPlugin } from "./plugins/markdown-shortcut/index.js";
31
+ import { EmptyTextNodeIndex, MDX_FOCUS_SEARCH_NAME, MDX_SEARCH_NAME, debouncedIndexer$, editorSearchCursor$, editorSearchRanges$, editorSearchScollableContent$, editorSearchTerm$, editorSearchTermDebounced$, editorSearchTextNodeIndex$, rangeSearchScan, searchOpen$, searchPlugin, useEditorSearch } from "./plugins/search/index.js";
31
32
  import { BlockTypeSelect } from "./plugins/toolbar/components/BlockTypeSelect.js";
32
33
  import { BoldItalicUnderlineToggles, StrikeThroughSupSubToggles } from "./plugins/toolbar/components/BoldItalicUnderlineToggles.js";
33
34
  import { ChangeAdmonitionType, admonitionLabelsMap } from "./plugins/toolbar/components/ChangeAdmonitionType.js";
@@ -108,6 +109,7 @@ export {
108
109
  DialogButton,
109
110
  DiffSourceToggleWrapper,
110
111
  DirectiveNode,
112
+ EmptyTextNodeIndex,
111
113
  FrontmatterNode,
112
114
  GenericDirectiveEditor,
113
115
  GenericHTMLNode,
@@ -133,6 +135,8 @@ export {
133
135
  KitchenSinkToolbar,
134
136
  ListsToggle,
135
137
  MDXEditor,
138
+ MDX_FOCUS_SEARCH_NAME,
139
+ MDX_SEARCH_NAME,
136
140
  MarkdownParseError,
137
141
  MultipleChoiceToggleGroup,
138
142
  NESTED_EDITOR_UPDATED_COMMAND,
@@ -210,6 +214,7 @@ export {
210
214
  currentSelection$,
211
215
  curry1to0,
212
216
  curry2to1,
217
+ debouncedIndexer$,
213
218
  defaultCodeBlockLanguage$,
214
219
  defaultSvgIcons,
215
220
  diffMarkdown$,
@@ -222,6 +227,12 @@ export {
222
227
  editImageToolbarComponent$,
223
228
  editorInFocus$,
224
229
  editorRootElementRef$,
230
+ editorSearchCursor$,
231
+ editorSearchRanges$,
232
+ editorSearchScollableContent$,
233
+ editorSearchTerm$,
234
+ editorSearchTermDebounced$,
235
+ editorSearchTextNodeIndex$,
225
236
  editorWrappers$,
226
237
  exportLexicalTreeToMdast,
227
238
  exportMarkdownFromLexical,
@@ -296,6 +307,7 @@ export {
296
307
  placeholder$,
297
308
  prop,
298
309
  quotePlugin,
310
+ rangeSearchScan,
299
311
  readOnly$,
300
312
  readOnlyDiff$,
301
313
  realmPlugin,
@@ -307,6 +319,8 @@ export {
307
319
  sandpackConfig$,
308
320
  sandpackPlugin,
309
321
  saveImage$,
322
+ searchOpen$,
323
+ searchPlugin,
310
324
  setMarkdown$,
311
325
  spellCheck$,
312
326
  switchFromPreviewToLinkEdit$,
@@ -324,6 +338,7 @@ export {
324
338
  translation$,
325
339
  updateLink$,
326
340
  useCodeBlockEditorContext,
341
+ useEditorSearch,
327
342
  useLexicalNodeRemove,
328
343
  useMdastNodeUpdater,
329
344
  useNestedEditorContext,
@@ -101,7 +101,7 @@ const mutableMarkdownSignal$ = Signal((r) => {
101
101
  ),
102
102
  mutableMarkdownSignal$
103
103
  );
104
- });
104
+ }, true);
105
105
  const importVisitors$ = Cell([]);
106
106
  const usedLexicalNodes$ = Cell([]);
107
107
  const syntaxExtensions$ = Cell([]);
@@ -475,10 +475,14 @@ const viewMode$ = Cell("rich-text", (r) => {
475
475
  }
476
476
  );
477
477
  });
478
- const markdownSourceEditorValue$ = Cell("", (r) => {
479
- r.link(markdown$, markdownSourceEditorValue$);
480
- r.link(markdownSourceEditorValue$, markdownSignal$);
481
- });
478
+ const markdownSourceEditorValue$ = Cell(
479
+ "",
480
+ (r) => {
481
+ r.link(markdown$, markdownSourceEditorValue$);
482
+ r.link(markdownSourceEditorValue$, markdownSignal$);
483
+ },
484
+ true
485
+ );
482
486
  const activePlugins$ = Cell([]);
483
487
  const addActivePlugin$ = Appender(activePlugins$);
484
488
  const translation$ = Cell(() => {
@@ -491,6 +495,7 @@ const corePlugin = realmPlugin({
491
495
  r.register(createRootEditorSubscription$);
492
496
  r.register(createActiveEditorSubscription$);
493
497
  r.register(markdownSignal$);
498
+ r.register(markdownSourceEditorValue$);
494
499
  r.pubIn({
495
500
  [initialMarkdown$]: (params == null ? void 0 : params.trim) ? initialMarkdown.trim() : initialMarkdown,
496
501
  [iconComponentFor$]: params == null ? void 0 : params.iconComponentFor,
@@ -0,0 +1,368 @@
1
+ import { Cell, debounceTime, useRealm, useCellValue, useCell } from "@mdxeditor/gurx";
2
+ import { getNearestEditorFromDOMNode, $getNearestNodeFromDOMNode, $isTextNode, $createRangeSelection } from "lexical";
3
+ import { realmPlugin } from "../../RealmWithPlugins.js";
4
+ import { contentEditableRef$, createRootEditorSubscription$ } from "../core/index.js";
5
+ const EmptyTextNodeIndex = {
6
+ allText: "",
7
+ nodeIndex: [],
8
+ offsetIndex: []
9
+ };
10
+ const editorSearchTerm$ = Cell("");
11
+ const editorSearchRanges$ = Cell([]);
12
+ const editorSearchCursor$ = Cell(0);
13
+ const editorSearchTextNodeIndex$ = Cell(EmptyTextNodeIndex);
14
+ const searchOpen$ = Cell(false);
15
+ const editorSearchTermDebounced$ = Cell("", (realm) => {
16
+ realm.link(editorSearchTermDebounced$, realm.pipe(editorSearchTerm$, realm.transformer(debounceTime(250))));
17
+ });
18
+ const editorSearchScollableContent$ = Cell(
19
+ null,
20
+ (r) => r.sub(contentEditableRef$, (cref) => {
21
+ var _a;
22
+ r.pub(editorSearchScollableContent$, ((_a = cref == null ? void 0 : cref.current) == null ? void 0 : _a.parentNode) ?? null);
23
+ })
24
+ );
25
+ const MDX_SEARCH_NAME = "MdxSearch";
26
+ const MDX_FOCUS_SEARCH_NAME = "MdxFocusSearch";
27
+ const debouncedIndexer$ = Cell(EmptyTextNodeIndex, (realm) => {
28
+ realm.link(debouncedIndexer$, realm.pipe(editorSearchTextNodeIndex$, realm.transformer(debounceTime(250))));
29
+ });
30
+ function* searchText(allText, searchQuery) {
31
+ if (!searchQuery) {
32
+ return;
33
+ }
34
+ let regex;
35
+ try {
36
+ regex = new RegExp(searchQuery, "gi");
37
+ } catch (e) {
38
+ console.error("Invalid search pattern:", e);
39
+ return;
40
+ }
41
+ let match;
42
+ while ((match = regex.exec(allText)) !== null) {
43
+ if (match[0].length === 0) {
44
+ if (regex.lastIndex === match.index) {
45
+ regex.lastIndex++;
46
+ }
47
+ continue;
48
+ }
49
+ const start = match.index;
50
+ const end = start + match[0].length - 1;
51
+ yield [start, end];
52
+ }
53
+ }
54
+ function indexAllTextNodes(root) {
55
+ var _a;
56
+ let allText = "";
57
+ const nodeIndex = [];
58
+ const offsetIndex = [];
59
+ if (!root) {
60
+ return { allText: "", nodeIndex, offsetIndex };
61
+ }
62
+ const contentSelector = "p, h1, h2, h3, h4, h5, h6, li, code, pre";
63
+ const treeWalker = document.createTreeWalker(
64
+ root,
65
+ NodeFilter.SHOW_TEXT,
66
+ // The corrected heuristic: accept any text node that is a descendant of a valid content container.
67
+ (node) => {
68
+ var _a2;
69
+ if ((_a2 = node.parentElement) == null ? void 0 : _a2.closest(contentSelector)) {
70
+ return NodeFilter.FILTER_ACCEPT;
71
+ }
72
+ return NodeFilter.FILTER_REJECT;
73
+ }
74
+ );
75
+ let currentNode;
76
+ while (currentNode = treeWalker.nextNode()) {
77
+ const nodeContent = ((_a = currentNode.textContent) == null ? void 0 : _a.normalize("NFKD")) ?? currentNode.textContent ?? "";
78
+ for (let i = 0; i < nodeContent.length; i++) {
79
+ nodeIndex.push(currentNode);
80
+ offsetIndex.push(i);
81
+ allText += nodeContent[i] ?? "";
82
+ }
83
+ }
84
+ return { allText, nodeIndex, offsetIndex };
85
+ }
86
+ function* rangeSearchScan(searchQuery, { allText, offsetIndex, nodeIndex }) {
87
+ for (const [start, end] of searchText(allText, searchQuery)) {
88
+ const startOffset = offsetIndex[start];
89
+ const endOffset = offsetIndex[end];
90
+ const startNode = nodeIndex[start];
91
+ const endNode = nodeIndex[end];
92
+ const range = new Range();
93
+ if (startNode === void 0 || endNode === void 0 || startOffset === void 0 || endOffset === void 0) {
94
+ throw new Error("Invalid range: startNode, endNode, startOffset, or endOffset is undefined.");
95
+ }
96
+ range.setStart(startNode, startOffset);
97
+ range.setEnd(endNode, endOffset + 1);
98
+ yield range;
99
+ }
100
+ }
101
+ const focusHighlightRange = (range) => {
102
+ CSS.highlights.delete(MDX_FOCUS_SEARCH_NAME);
103
+ if (range)
104
+ CSS.highlights.set(MDX_FOCUS_SEARCH_NAME, new Highlight(range));
105
+ };
106
+ const highlightRanges = (ranges) => {
107
+ CSS.highlights.set(MDX_SEARCH_NAME, new Highlight(...ranges));
108
+ };
109
+ const resetHighlights = () => {
110
+ CSS.highlights.delete(MDX_SEARCH_NAME);
111
+ CSS.highlights.delete(MDX_FOCUS_SEARCH_NAME);
112
+ };
113
+ const scrollToRange = (range, contentEditable, options) => {
114
+ const ignoreIfInView = (options == null ? void 0 : options.ignoreIfInView) ?? true;
115
+ const behavior = (options == null ? void 0 : options.behavior) ?? "smooth";
116
+ const [first] = range.getClientRects();
117
+ if (!contentEditable) {
118
+ console.warn("No content-editable element found for scrolling.");
119
+ return;
120
+ }
121
+ if (!first) {
122
+ console.warn("No client rect found for the range, cannot scroll.");
123
+ return;
124
+ }
125
+ const containerRect = contentEditable.getBoundingClientRect();
126
+ const topRelativeToContainer = first.top - containerRect.top;
127
+ const bottomRelativeToContainer = first.bottom - containerRect.top;
128
+ if (ignoreIfInView) {
129
+ const rangeTop = topRelativeToContainer + contentEditable.scrollTop;
130
+ const rangeBottom = bottomRelativeToContainer + contentEditable.scrollTop;
131
+ const visibleTop = contentEditable.scrollTop;
132
+ const visibleBottom = visibleTop + contentEditable.clientHeight;
133
+ const inView = rangeTop >= visibleTop && rangeBottom <= visibleBottom;
134
+ if (inView)
135
+ return;
136
+ }
137
+ const top = topRelativeToContainer + contentEditable.scrollTop - first.height;
138
+ contentEditable.scrollTo({ top, behavior });
139
+ };
140
+ function isSimilarRange(range1, range2) {
141
+ return range1.startContainer === range2.startContainer && range1.startOffset === range2.startOffset;
142
+ }
143
+ function replaceTextInRange(range, str, onUpdate) {
144
+ const startDomNode = range.startContainer;
145
+ const endDomNode = range.endContainer;
146
+ const startOffset = range.startOffset;
147
+ const endOffset = range.endOffset;
148
+ const editor = getNearestEditorFromDOMNode(startDomNode);
149
+ if (!editor) {
150
+ console.warn("No editor found for the provided DOM node.");
151
+ return;
152
+ }
153
+ editor.update(
154
+ () => {
155
+ const startLexicalNode = $getNearestNodeFromDOMNode(startDomNode);
156
+ const endLexicalNode = $getNearestNodeFromDOMNode(endDomNode);
157
+ if (!$isTextNode(startLexicalNode) || !$isTextNode(endLexicalNode)) {
158
+ return;
159
+ }
160
+ try {
161
+ const selection = $createRangeSelection();
162
+ selection.anchor.set(startLexicalNode.getKey(), startOffset, "text");
163
+ selection.focus.set(endLexicalNode.getKey(), endOffset, "text");
164
+ selection.insertText(str);
165
+ } catch (e) {
166
+ console.warn("Error replacing text in the editor:", e);
167
+ if (onUpdate) {
168
+ onUpdate();
169
+ }
170
+ }
171
+ },
172
+ {
173
+ onUpdate
174
+ }
175
+ );
176
+ }
177
+ function useEditorSearch() {
178
+ const realm = useRealm();
179
+ const ranges = useCellValue(editorSearchRanges$);
180
+ const cursor = useCellValue(editorSearchCursor$);
181
+ const search = useCellValue(editorSearchTerm$);
182
+ const currentRange = ranges[cursor - 1] ?? null;
183
+ const contentEditable = useCellValue(editorSearchScollableContent$);
184
+ const [isSearchOpen, setIsSearchOpen] = useCell(searchOpen$);
185
+ const openSearch = () => {
186
+ setIsSearchOpen(true);
187
+ };
188
+ const closeSearch = () => {
189
+ setIsSearchOpen(false);
190
+ };
191
+ const toggleSearch = () => {
192
+ setIsSearchOpen(!isSearchOpen);
193
+ };
194
+ const rangeCount = ranges.length;
195
+ const scrollToRangeOrIndex = (range, options) => {
196
+ const scrollRange = typeof range === "number" ? ranges[range - 1] : range;
197
+ if (!scrollRange) {
198
+ throw new Error("Error scrolling to range, range does not exist");
199
+ }
200
+ scrollToRange(scrollRange, contentEditable, options);
201
+ };
202
+ const setSearch = (term) => {
203
+ if ((term ?? "") !== search) {
204
+ realm.pub(editorSearchCursor$, 0);
205
+ }
206
+ realm.pub(editorSearchTermDebounced$, term ?? "");
207
+ };
208
+ const next = () => {
209
+ if (!ranges.length)
210
+ return;
211
+ const newVal = cursor % ranges.length + 1;
212
+ scrollToRangeOrIndex(newVal);
213
+ realm.pub(editorSearchCursor$, newVal);
214
+ };
215
+ const prev = () => {
216
+ if (!ranges.length)
217
+ return;
218
+ const newVal = cursor <= 1 ? ranges.length : cursor - 1;
219
+ scrollToRangeOrIndex(newVal);
220
+ realm.pub(editorSearchCursor$, newVal);
221
+ };
222
+ const replace = (str, onUpdate) => {
223
+ const currentRange2 = ranges[cursor - 1];
224
+ if (!currentRange2) {
225
+ return;
226
+ }
227
+ const { startContainer, startOffset } = currentRange2 ?? {};
228
+ replaceTextInRange(currentRange2, str, () => {
229
+ const unsub = realm.sub(editorSearchRanges$, (newRanges) => {
230
+ unsub();
231
+ if (isSimilarRange(newRanges[cursor - 1] ?? {}, {
232
+ startOffset,
233
+ startContainer
234
+ })) {
235
+ realm.pub(editorSearchCursor$, (cursor + 1) % (newRanges.length + 1) || 1);
236
+ }
237
+ });
238
+ onUpdate == null ? void 0 : onUpdate();
239
+ });
240
+ };
241
+ const replaceAll = (str, onUpdate) => {
242
+ const runReplaceAll = () => {
243
+ let ticks = 0;
244
+ for (let i = ranges.length - 1; i >= 0; i--) {
245
+ const textReplaceRange = ranges[i];
246
+ if (!textReplaceRange) {
247
+ throw new Error("error replacing all text range does not exist");
248
+ }
249
+ replaceTextInRange(textReplaceRange, str, () => {
250
+ ticks++;
251
+ if (ticks >= ranges.length) {
252
+ onUpdate == null ? void 0 : onUpdate();
253
+ }
254
+ });
255
+ }
256
+ };
257
+ if (typeof requestIdleCallback === "function") {
258
+ requestIdleCallback(runReplaceAll);
259
+ } else {
260
+ setTimeout(runReplaceAll, 0);
261
+ }
262
+ };
263
+ return {
264
+ next,
265
+ prev,
266
+ total: rangeCount,
267
+ cursor,
268
+ setSearch,
269
+ search,
270
+ currentRange,
271
+ isSearchOpen,
272
+ setIsSearchOpen,
273
+ openSearch,
274
+ closeSearch,
275
+ toggleSearch,
276
+ ranges,
277
+ scrollToRangeOrIndex,
278
+ replace,
279
+ replaceAll
280
+ };
281
+ }
282
+ const searchPlugin = realmPlugin({
283
+ //TODO: ensure proper event cleanup
284
+ init(realm) {
285
+ if (typeof CSS.highlights === "undefined") {
286
+ console.warn("CSS.highlights is not supported in this browser. Search functionality will be limited.");
287
+ return;
288
+ }
289
+ realm.sub(editorSearchCursor$, (cursor) => {
290
+ const ranges = realm.getValue(editorSearchRanges$);
291
+ focusHighlightRange(ranges[cursor - 1]);
292
+ });
293
+ const updateHighlights = (searchQuery, textNodeIndex) => {
294
+ if (!searchQuery) {
295
+ realm.pub(editorSearchCursor$, 0);
296
+ realm.pub(editorSearchRanges$, []);
297
+ resetHighlights();
298
+ return;
299
+ }
300
+ const ranges = Array.from(rangeSearchScan(searchQuery, textNodeIndex));
301
+ realm.pub(editorSearchRanges$, ranges);
302
+ highlightRanges(ranges);
303
+ if (ranges.length) {
304
+ const currentCursor = realm.getValue(editorSearchCursor$) || 1;
305
+ focusHighlightRange(ranges[currentCursor - 1]);
306
+ realm.pub(editorSearchCursor$, currentCursor);
307
+ const scrollRange = ranges[currentCursor - 1];
308
+ if (!scrollRange)
309
+ throw new Error("error updating highlights, scroll range does not exist");
310
+ const contentEditable = realm.getValue(editorSearchScollableContent$);
311
+ scrollToRange(scrollRange, contentEditable, {
312
+ ignoreIfInView: true
313
+ });
314
+ } else {
315
+ resetHighlights();
316
+ }
317
+ };
318
+ realm.sub(editorSearchTextNodeIndex$, (textNodeIndex) => {
319
+ updateHighlights(realm.getValue(editorSearchTerm$), textNodeIndex);
320
+ });
321
+ realm.sub(editorSearchTerm$, (searchQuery) => {
322
+ updateHighlights(searchQuery, realm.getValue(editorSearchTextNodeIndex$));
323
+ });
324
+ realm.pub(createRootEditorSubscription$, (editor) => {
325
+ let observer = null;
326
+ return editor.registerRootListener((rootElement) => {
327
+ if (observer) {
328
+ observer.disconnect();
329
+ observer = null;
330
+ }
331
+ if (rootElement) {
332
+ const initialIndex = indexAllTextNodes(rootElement);
333
+ realm.pub(editorSearchTextNodeIndex$, initialIndex);
334
+ observer = new MutationObserver(() => {
335
+ const newIndex = indexAllTextNodes(rootElement);
336
+ if (realm.getValue(searchOpen$)) {
337
+ realm.pub(editorSearchTextNodeIndex$, newIndex);
338
+ } else {
339
+ realm.pub(debouncedIndexer$, newIndex);
340
+ }
341
+ });
342
+ observer.observe(rootElement, {
343
+ childList: true,
344
+ subtree: true,
345
+ characterData: true
346
+ });
347
+ return () => observer == null ? void 0 : observer.disconnect();
348
+ }
349
+ });
350
+ });
351
+ }
352
+ });
353
+ export {
354
+ EmptyTextNodeIndex,
355
+ MDX_FOCUS_SEARCH_NAME,
356
+ MDX_SEARCH_NAME,
357
+ debouncedIndexer$,
358
+ editorSearchCursor$,
359
+ editorSearchRanges$,
360
+ editorSearchScollableContent$,
361
+ editorSearchTerm$,
362
+ editorSearchTermDebounced$,
363
+ editorSearchTextNodeIndex$,
364
+ rangeSearchScan,
365
+ searchOpen$,
366
+ searchPlugin,
367
+ useEditorSearch
368
+ };
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "*.css"
12
12
  ],
13
13
  "type": "module",
14
- "version": "3.39.0",
14
+ "version": "3.40.0",
15
15
  "description": "React component for rich text markdown editing",
16
16
  "scripts": {
17
17
  "build": "vite build",