@atlaskit/editor-plugin-autocomplete 4.0.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @atlaskit/editor-plugin-autocomplete
2
2
 
3
+ ## 4.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [`56263a9d5bb6d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/56263a9d5bb6d) -
8
+ Gate editor autocomplete to English locales so suggestions and model loading are disabled for
9
+ non-English users.
10
+
11
+ ## 4.0.1
12
+
13
+ ### Patch Changes
14
+
15
+ - [`560ddb7a914f6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/560ddb7a914f6) -
16
+ [ux] Fixes a bug where autocomplete suggestions were shown when the cursor was positioned in the
17
+ middle of an existing word. Suggestions are now suppressed unless the cursor is at the trailing
18
+ edge of a token.
19
+ - Updated dependencies
20
+
3
21
  ## 4.0.0
4
22
 
5
23
  ### Major Changes
@@ -188,6 +188,38 @@ var buildSlowLaneText = function buildSlowLaneText(docText, context) {
188
188
  lines.push("reply ".concat(nextReplyNumber, ": ").concat(docText));
189
189
  return lines.join('\n');
190
190
  };
191
+ var createMidWordCharacterRegex = function createMidWordCharacterRegex() {
192
+ try {
193
+ // string-built regex avoids TS1501 under the package's ES5 build target
194
+ return new RegExp('[\\p{L}\\p{N}_]', 'u');
195
+ } catch (_unused) {
196
+ return null;
197
+ }
198
+ };
199
+ var midWordCharacterRegex = createMidWordCharacterRegex();
200
+ var isAsciiWordCharacter = function isAsciiWordCharacter(char) {
201
+ if (char === '_') {
202
+ return true;
203
+ }
204
+ var charCode = char.charCodeAt(0);
205
+ return charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122;
206
+ };
207
+ var isMidWordCharacter = function isMidWordCharacter(char) {
208
+ if (midWordCharacterRegex) {
209
+ return midWordCharacterRegex.test(char);
210
+ }
211
+ if (isAsciiWordCharacter(char)) {
212
+ return true;
213
+ }
214
+ return char.toLocaleLowerCase() !== char.toLocaleUpperCase();
215
+ };
216
+ var getLeadingTextCharacter = function getLeadingTextCharacter(text) {
217
+ var firstCodePoint = text === null || text === void 0 ? void 0 : text.codePointAt(0);
218
+ if (firstCodePoint === undefined) {
219
+ return undefined;
220
+ }
221
+ return String.fromCodePoint(firstCodePoint);
222
+ };
191
223
  var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefore) {
192
224
  var _textBefore$match$, _textBefore$match;
193
225
  if ((0, _slowLaneClient.isWordBoundary)(textBefore)) {
@@ -198,7 +230,16 @@ var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefor
198
230
  var trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(/([^\s.,;:!?]*)$/)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
199
231
  return trailingToken.length;
200
232
  };
233
+ var isEnglishLocale = function isEnglishLocale(locale) {
234
+ if (!locale) {
235
+ return false;
236
+ }
237
+ return locale.toLowerCase().startsWith('en');
238
+ };
201
239
  var createAutocompletePlugin = exports.createAutocompletePlugin = function createAutocompletePlugin(options, api) {
240
+ var _options$locale;
241
+ var locale = (_options$locale = options === null || options === void 0 ? void 0 : options.locale) !== null && _options$locale !== void 0 ? _options$locale : typeof navigator !== 'undefined' ? navigator.language : undefined;
242
+ var isAutocompleteEnabled = isEnglishLocale(locale);
202
243
  var debounceTimer = null;
203
244
  var hasIngestedPage = false;
204
245
  var resolvedContext;
@@ -440,6 +481,19 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
440
481
  if (!selection.empty) {
441
482
  return;
442
483
  }
484
+
485
+ // Suppress suggestions when the cursor is mid-word — only offer
486
+ // completions when the cursor is at the trailing edge of a token.
487
+ // nodeAfter correctly handles inline atoms (mentions, emojis) where
488
+ // parentOffset and textContent indices diverge.
489
+ var nodeAfter = selection.$from.nodeAfter;
490
+ var charAfterCursor = nodeAfter !== null && nodeAfter !== void 0 && nodeAfter.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
491
+ // Only suppress for Unicode letters, digits, and underscore — hyphens,
492
+ // apostrophes, and similar punctuation are valid left-edge boundaries
493
+ // and should not block suggestions (e.g. cursor before '-' in compound-word).
494
+ if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
495
+ return;
496
+ }
443
497
  var textBefore = getTextBeforeCursor(state);
444
498
 
445
499
  // Suppress re-showing the same suggestion the user just dismissed.
@@ -575,6 +629,9 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
575
629
  }),
576
630
  handleDOMEvents: {
577
631
  blur: function blur(view) {
632
+ if (!isAutocompleteEnabled) {
633
+ return false;
634
+ }
578
635
  var pluginState = autocompletePluginKey.getState(view.state);
579
636
  if (pluginState !== null && pluginState !== void 0 && pluginState.ghostText) {
580
637
  clearGhostText(view.state, view.dispatch);
@@ -584,6 +641,9 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
584
641
  },
585
642
  focus: function focus() {
586
643
  var _options$useLocalMode, _options$useLocalMode2;
644
+ if (!isAutocompleteEnabled) {
645
+ return false;
646
+ }
587
647
  (0, _textPredictor.loadDefaultVocabulary)({
588
648
  isLocalLLM: (_options$useLocalMode = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode !== void 0 ? _options$useLocalMode : false
589
649
  }).catch(function (error) {
@@ -613,6 +673,9 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
613
673
  view: function view() {
614
674
  return {
615
675
  update: function update(view, prevState) {
676
+ if (!isAutocompleteEnabled) {
677
+ return;
678
+ }
616
679
  currentView = view;
617
680
  if (!prevState.doc.eq(view.state.doc)) {
618
681
  if (justAccepted) {
@@ -176,6 +176,38 @@ const buildSlowLaneText = (docText, context) => {
176
176
  lines.push(`reply ${nextReplyNumber}: ${docText}`);
177
177
  return lines.join('\n');
178
178
  };
179
+ const createMidWordCharacterRegex = () => {
180
+ try {
181
+ // string-built regex avoids TS1501 under the package's ES5 build target
182
+ return new RegExp('[\\p{L}\\p{N}_]', 'u');
183
+ } catch {
184
+ return null;
185
+ }
186
+ };
187
+ const midWordCharacterRegex = createMidWordCharacterRegex();
188
+ const isAsciiWordCharacter = char => {
189
+ if (char === '_') {
190
+ return true;
191
+ }
192
+ const charCode = char.charCodeAt(0);
193
+ return charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122;
194
+ };
195
+ const isMidWordCharacter = char => {
196
+ if (midWordCharacterRegex) {
197
+ return midWordCharacterRegex.test(char);
198
+ }
199
+ if (isAsciiWordCharacter(char)) {
200
+ return true;
201
+ }
202
+ return char.toLocaleLowerCase() !== char.toLocaleUpperCase();
203
+ };
204
+ const getLeadingTextCharacter = text => {
205
+ const firstCodePoint = text === null || text === void 0 ? void 0 : text.codePointAt(0);
206
+ if (firstCodePoint === undefined) {
207
+ return undefined;
208
+ }
209
+ return String.fromCodePoint(firstCodePoint);
210
+ };
179
211
  const getTypedLengthForPrediction = textBefore => {
180
212
  var _textBefore$match$, _textBefore$match;
181
213
  if (isWordBoundary(textBefore)) {
@@ -186,7 +218,16 @@ const getTypedLengthForPrediction = textBefore => {
186
218
  const trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(/([^\s.,;:!?]*)$/)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
187
219
  return trailingToken.length;
188
220
  };
221
+ const isEnglishLocale = locale => {
222
+ if (!locale) {
223
+ return false;
224
+ }
225
+ return locale.toLowerCase().startsWith('en');
226
+ };
189
227
  export const createAutocompletePlugin = (options, api) => {
228
+ var _options$locale;
229
+ const locale = (_options$locale = options === null || options === void 0 ? void 0 : options.locale) !== null && _options$locale !== void 0 ? _options$locale : typeof navigator !== 'undefined' ? navigator.language : undefined;
230
+ const isAutocompleteEnabled = isEnglishLocale(locale);
190
231
  let debounceTimer = null;
191
232
  let hasIngestedPage = false;
192
233
  let resolvedContext;
@@ -424,6 +465,19 @@ export const createAutocompletePlugin = (options, api) => {
424
465
  if (!selection.empty) {
425
466
  return;
426
467
  }
468
+
469
+ // Suppress suggestions when the cursor is mid-word — only offer
470
+ // completions when the cursor is at the trailing edge of a token.
471
+ // nodeAfter correctly handles inline atoms (mentions, emojis) where
472
+ // parentOffset and textContent indices diverge.
473
+ const nodeAfter = selection.$from.nodeAfter;
474
+ const charAfterCursor = nodeAfter !== null && nodeAfter !== void 0 && nodeAfter.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
475
+ // Only suppress for Unicode letters, digits, and underscore — hyphens,
476
+ // apostrophes, and similar punctuation are valid left-edge boundaries
477
+ // and should not block suggestions (e.g. cursor before '-' in compound-word).
478
+ if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
479
+ return;
480
+ }
427
481
  const textBefore = getTextBeforeCursor(state);
428
482
 
429
483
  // Suppress re-showing the same suggestion the user just dismissed.
@@ -562,6 +616,9 @@ export const createAutocompletePlugin = (options, api) => {
562
616
  }),
563
617
  handleDOMEvents: {
564
618
  blur: view => {
619
+ if (!isAutocompleteEnabled) {
620
+ return false;
621
+ }
565
622
  const pluginState = autocompletePluginKey.getState(view.state);
566
623
  if (pluginState !== null && pluginState !== void 0 && pluginState.ghostText) {
567
624
  clearGhostText(view.state, view.dispatch);
@@ -571,6 +628,9 @@ export const createAutocompletePlugin = (options, api) => {
571
628
  },
572
629
  focus: () => {
573
630
  var _options$useLocalMode, _options$useLocalMode2;
631
+ if (!isAutocompleteEnabled) {
632
+ return false;
633
+ }
574
634
  loadDefaultVocabulary({
575
635
  isLocalLLM: (_options$useLocalMode = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode !== void 0 ? _options$useLocalMode : false
576
636
  }).catch(error => {
@@ -599,6 +659,9 @@ export const createAutocompletePlugin = (options, api) => {
599
659
  },
600
660
  view: () => ({
601
661
  update: (view, prevState) => {
662
+ if (!isAutocompleteEnabled) {
663
+ return;
664
+ }
602
665
  currentView = view;
603
666
  if (!prevState.doc.eq(view.state.doc)) {
604
667
  if (justAccepted) {
@@ -181,6 +181,38 @@ var buildSlowLaneText = function buildSlowLaneText(docText, context) {
181
181
  lines.push("reply ".concat(nextReplyNumber, ": ").concat(docText));
182
182
  return lines.join('\n');
183
183
  };
184
+ var createMidWordCharacterRegex = function createMidWordCharacterRegex() {
185
+ try {
186
+ // string-built regex avoids TS1501 under the package's ES5 build target
187
+ return new RegExp('[\\p{L}\\p{N}_]', 'u');
188
+ } catch (_unused) {
189
+ return null;
190
+ }
191
+ };
192
+ var midWordCharacterRegex = createMidWordCharacterRegex();
193
+ var isAsciiWordCharacter = function isAsciiWordCharacter(char) {
194
+ if (char === '_') {
195
+ return true;
196
+ }
197
+ var charCode = char.charCodeAt(0);
198
+ return charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122;
199
+ };
200
+ var isMidWordCharacter = function isMidWordCharacter(char) {
201
+ if (midWordCharacterRegex) {
202
+ return midWordCharacterRegex.test(char);
203
+ }
204
+ if (isAsciiWordCharacter(char)) {
205
+ return true;
206
+ }
207
+ return char.toLocaleLowerCase() !== char.toLocaleUpperCase();
208
+ };
209
+ var getLeadingTextCharacter = function getLeadingTextCharacter(text) {
210
+ var firstCodePoint = text === null || text === void 0 ? void 0 : text.codePointAt(0);
211
+ if (firstCodePoint === undefined) {
212
+ return undefined;
213
+ }
214
+ return String.fromCodePoint(firstCodePoint);
215
+ };
184
216
  var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefore) {
185
217
  var _textBefore$match$, _textBefore$match;
186
218
  if (isWordBoundary(textBefore)) {
@@ -191,7 +223,16 @@ var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefor
191
223
  var trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(/([^\s.,;:!?]*)$/)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
192
224
  return trailingToken.length;
193
225
  };
226
+ var isEnglishLocale = function isEnglishLocale(locale) {
227
+ if (!locale) {
228
+ return false;
229
+ }
230
+ return locale.toLowerCase().startsWith('en');
231
+ };
194
232
  export var createAutocompletePlugin = function createAutocompletePlugin(options, api) {
233
+ var _options$locale;
234
+ var locale = (_options$locale = options === null || options === void 0 ? void 0 : options.locale) !== null && _options$locale !== void 0 ? _options$locale : typeof navigator !== 'undefined' ? navigator.language : undefined;
235
+ var isAutocompleteEnabled = isEnglishLocale(locale);
195
236
  var debounceTimer = null;
196
237
  var hasIngestedPage = false;
197
238
  var resolvedContext;
@@ -433,6 +474,19 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
433
474
  if (!selection.empty) {
434
475
  return;
435
476
  }
477
+
478
+ // Suppress suggestions when the cursor is mid-word — only offer
479
+ // completions when the cursor is at the trailing edge of a token.
480
+ // nodeAfter correctly handles inline atoms (mentions, emojis) where
481
+ // parentOffset and textContent indices diverge.
482
+ var nodeAfter = selection.$from.nodeAfter;
483
+ var charAfterCursor = nodeAfter !== null && nodeAfter !== void 0 && nodeAfter.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
484
+ // Only suppress for Unicode letters, digits, and underscore — hyphens,
485
+ // apostrophes, and similar punctuation are valid left-edge boundaries
486
+ // and should not block suggestions (e.g. cursor before '-' in compound-word).
487
+ if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
488
+ return;
489
+ }
436
490
  var textBefore = getTextBeforeCursor(state);
437
491
 
438
492
  // Suppress re-showing the same suggestion the user just dismissed.
@@ -568,6 +622,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
568
622
  }),
569
623
  handleDOMEvents: {
570
624
  blur: function blur(view) {
625
+ if (!isAutocompleteEnabled) {
626
+ return false;
627
+ }
571
628
  var pluginState = autocompletePluginKey.getState(view.state);
572
629
  if (pluginState !== null && pluginState !== void 0 && pluginState.ghostText) {
573
630
  clearGhostText(view.state, view.dispatch);
@@ -577,6 +634,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
577
634
  },
578
635
  focus: function focus() {
579
636
  var _options$useLocalMode, _options$useLocalMode2;
637
+ if (!isAutocompleteEnabled) {
638
+ return false;
639
+ }
580
640
  loadDefaultVocabulary({
581
641
  isLocalLLM: (_options$useLocalMode = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode !== void 0 ? _options$useLocalMode : false
582
642
  }).catch(function (error) {
@@ -606,6 +666,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
606
666
  view: function view() {
607
667
  return {
608
668
  update: function update(view, prevState) {
669
+ if (!isAutocompleteEnabled) {
670
+ return;
671
+ }
609
672
  currentView = view;
610
673
  if (!prevState.doc.eq(view.state.doc)) {
611
674
  if (justAccepted) {
@@ -40,6 +40,11 @@ export interface AutocompletePluginOptions {
40
40
  * Use this to serve vectors from a CDN or media service in production.
41
41
  */
42
42
  getVectorsBinaryUrl?: () => Promise<string>;
43
+ /**
44
+ * User locale used to determine whether autocomplete should run.
45
+ * Defaults to browser locale when omitted.
46
+ */
47
+ locale?: string;
43
48
  /**
44
49
  * When true, uses on-device inference via WebGPU (MLC WebLLM) instead of
45
50
  * the network-based slow-lane backend. Defaults to false (network client).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-autocomplete",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
4
  "description": "Client-side text autocomplete plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -27,7 +27,7 @@
27
27
  "wink-nlp": "^2.4.0"
28
28
  },
29
29
  "peerDependencies": {
30
- "@atlaskit/editor-common": "^116.0.0",
30
+ "@atlaskit/editor-common": "^116.4.0",
31
31
  "@atlaskit/editor-plugin-analytics": "^12.0.0",
32
32
  "react": "^18.2.0"
33
33
  },
@@ -214,6 +214,11 @@ export interface AutocompletePluginOptions {
214
214
  * Use this to serve vectors from a CDN or media service in production.
215
215
  */
216
216
  getVectorsBinaryUrl?: () => Promise<string>;
217
+ /**
218
+ * User locale used to determine whether autocomplete should run.
219
+ * Defaults to browser locale when omitted.
220
+ */
221
+ locale?: string;
217
222
  /**
218
223
  * When true, uses on-device inference via WebGPU (MLC WebLLM) instead of
219
224
  * the network-based slow-lane backend. Defaults to false (network client).
@@ -243,6 +248,52 @@ const buildSlowLaneText = (docText: string, context?: AutocompleteContext): stri
243
248
  return lines.join('\n');
244
249
  };
245
250
 
251
+ const createMidWordCharacterRegex = (): RegExp | null => {
252
+ try {
253
+ // string-built regex avoids TS1501 under the package's ES5 build target
254
+ return new RegExp('[\\p{L}\\p{N}_]', 'u');
255
+ } catch {
256
+ return null;
257
+ }
258
+ };
259
+
260
+ const midWordCharacterRegex = createMidWordCharacterRegex();
261
+
262
+ const isAsciiWordCharacter = (char: string): boolean => {
263
+ if (char === '_') {
264
+ return true;
265
+ }
266
+
267
+ const charCode = char.charCodeAt(0);
268
+ return (
269
+ (charCode >= 48 && charCode <= 57) ||
270
+ (charCode >= 65 && charCode <= 90) ||
271
+ (charCode >= 97 && charCode <= 122)
272
+ );
273
+ };
274
+
275
+ const isMidWordCharacter = (char: string): boolean => {
276
+ if (midWordCharacterRegex) {
277
+ return midWordCharacterRegex.test(char);
278
+ }
279
+
280
+ if (isAsciiWordCharacter(char)) {
281
+ return true;
282
+ }
283
+
284
+ return char.toLocaleLowerCase() !== char.toLocaleUpperCase();
285
+ };
286
+
287
+ const getLeadingTextCharacter = (text?: string): string | undefined => {
288
+ const firstCodePoint = text?.codePointAt(0);
289
+
290
+ if (firstCodePoint === undefined) {
291
+ return undefined;
292
+ }
293
+
294
+ return String.fromCodePoint(firstCodePoint);
295
+ };
296
+
246
297
  const getTypedLengthForPrediction = (textBefore: string): number => {
247
298
  if (isWordBoundary(textBefore)) {
248
299
  return 0;
@@ -253,10 +304,22 @@ const getTypedLengthForPrediction = (textBefore: string): number => {
253
304
  return trailingToken.length;
254
305
  };
255
306
 
307
+ const isEnglishLocale = (locale?: string): boolean => {
308
+ if (!locale) {
309
+ return false;
310
+ }
311
+
312
+ return locale.toLowerCase().startsWith('en');
313
+ };
314
+
256
315
  export const createAutocompletePlugin = (
257
316
  options?: AutocompletePluginOptions,
258
317
  api?: ExtractInjectionAPI<AutocompletePlugin>,
259
318
  ): SafePlugin<AutocompletePluginState> => {
319
+ const locale =
320
+ options?.locale ?? (typeof navigator !== 'undefined' ? navigator.language : undefined);
321
+ const isAutocompleteEnabled = isEnglishLocale(locale);
322
+
260
323
  let debounceTimer: ReturnType<typeof setTimeout> | null = null;
261
324
  let hasIngestedPage = false;
262
325
  let resolvedContext: AutocompleteContext | undefined;
@@ -514,6 +577,19 @@ export const createAutocompletePlugin = (
514
577
  return;
515
578
  }
516
579
 
580
+ // Suppress suggestions when the cursor is mid-word — only offer
581
+ // completions when the cursor is at the trailing edge of a token.
582
+ // nodeAfter correctly handles inline atoms (mentions, emojis) where
583
+ // parentOffset and textContent indices diverge.
584
+ const nodeAfter = selection.$from.nodeAfter;
585
+ const charAfterCursor = nodeAfter?.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
586
+ // Only suppress for Unicode letters, digits, and underscore — hyphens,
587
+ // apostrophes, and similar punctuation are valid left-edge boundaries
588
+ // and should not block suggestions (e.g. cursor before '-' in compound-word).
589
+ if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
590
+ return;
591
+ }
592
+
517
593
  const textBefore = getTextBeforeCursor(state);
518
594
 
519
595
  // Suppress re-showing the same suggestion the user just dismissed.
@@ -671,6 +747,10 @@ export const createAutocompletePlugin = (
671
747
 
672
748
  handleDOMEvents: {
673
749
  blur: (view: EditorView) => {
750
+ if (!isAutocompleteEnabled) {
751
+ return false;
752
+ }
753
+
674
754
  const pluginState = autocompletePluginKey.getState(view.state) as
675
755
  | AutocompletePluginState
676
756
  | undefined;
@@ -681,6 +761,10 @@ export const createAutocompletePlugin = (
681
761
  return false;
682
762
  },
683
763
  focus: () => {
764
+ if (!isAutocompleteEnabled) {
765
+ return false;
766
+ }
767
+
684
768
  loadDefaultVocabulary({ isLocalLLM: options?.useLocalModel ?? false }).catch((error) => {
685
769
  logException(error as Error, {
686
770
  location: 'editor-plugin-autocomplete/loadDefaultVocabulary',
@@ -708,6 +792,10 @@ export const createAutocompletePlugin = (
708
792
 
709
793
  view: () => ({
710
794
  update: (view: EditorView, prevState: EditorState) => {
795
+ if (!isAutocompleteEnabled) {
796
+ return;
797
+ }
798
+
711
799
  currentView = view;
712
800
  if (!prevState.doc.eq(view.state.doc)) {
713
801
  if (justAccepted) {