@atlaskit/editor-plugin-autocomplete 0.2.0 → 0.4.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/afm-cc/tsconfig.json +1 -4
  3. package/afm-jira/tsconfig.json +1 -4
  4. package/afm-products/tsconfig.json +1 -4
  5. package/build/tsconfig.json +1 -6
  6. package/dist/cjs/pm-plugins/autocomplete-plugin.js +7 -3
  7. package/dist/cjs/pm-plugins/scoring-pipeline.js +1 -3
  8. package/dist/cjs/pm-plugins/slow-lane-client.js +3 -1
  9. package/dist/cjs/pm-plugins/text-predictor.js +47 -23
  10. package/dist/es2019/pm-plugins/autocomplete-plugin.js +7 -3
  11. package/dist/es2019/pm-plugins/scoring-pipeline.js +0 -3
  12. package/dist/es2019/pm-plugins/slow-lane-client.js +5 -2
  13. package/dist/es2019/pm-plugins/text-predictor.js +24 -9
  14. package/dist/esm/pm-plugins/autocomplete-plugin.js +7 -3
  15. package/dist/esm/pm-plugins/scoring-pipeline.js +0 -3
  16. package/dist/esm/pm-plugins/slow-lane-client.js +3 -1
  17. package/dist/esm/pm-plugins/text-predictor.js +47 -23
  18. package/dist/types/pm-plugins/autocomplete-plugin.d.ts +6 -0
  19. package/dist/types/pm-plugins/text-predictor.d.ts +1 -1
  20. package/dist/types-ts4.5/pm-plugins/autocomplete-plugin.d.ts +6 -0
  21. package/dist/types-ts4.5/pm-plugins/text-predictor.d.ts +1 -1
  22. package/package.json +2 -2
  23. package/src/pm-plugins/autocomplete-plugin.ts +20 -5
  24. package/src/pm-plugins/data/combined_l2_l3_pos_tags.json +73571 -3
  25. package/src/pm-plugins/data/ghost_pos_tags.json +43 -3
  26. package/src/pm-plugins/data/grammar_transitions_10k.json +46 -3
  27. package/src/pm-plugins/data/l3_vocabulary.json +20002 -3
  28. package/src/pm-plugins/data/vocabulary_10k.json +38794 -3
  29. package/src/pm-plugins/data/word_index_10k.json +7760 -3
  30. package/src/pm-plugins/scoring-pipeline.ts +0 -3
  31. package/src/pm-plugins/slow-lane-client.ts +4 -2
  32. package/src/pm-plugins/text-predictor.ts +30 -10
  33. package/tsconfig.app.json +2 -2
  34. package/tsconfig.json +1 -1
  35. package/src/pm-plugins/typings.d.ts +0 -12
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/editor-plugin-autocomplete
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`603cd44e7b8c3`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/603cd44e7b8c3) -
8
+ Fetch vectors through media client
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies
13
+
14
+ ## 0.3.0
15
+
16
+ ### Minor Changes
17
+
18
+ - [`87965237565b6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/87965237565b6) -
19
+ [ux] Add autocomplete plugin to inline comment editor behind
20
+ `platform_editor_ai_autocomplete_conf_comments` experiment
21
+
3
22
  ## 0.2.0
4
23
 
5
24
  ### Minor Changes
@@ -6,10 +6,7 @@
6
6
  "rootDir": "../",
7
7
  "composite": true,
8
8
  "noCheck": true,
9
- // resolveJsonModule must remain false: this package contains LFS-tracked JSON data files
10
- // that appear as pointer text (not valid JSON) in CI environments where LFS is skipped.
11
- // Enabling resolveJsonModule would cause TS to attempt parsing these files and fail.
12
- "resolveJsonModule": false
9
+ "resolveJsonModule": true
13
10
  },
14
11
  "include": [
15
12
  "../src/**/*.ts",
@@ -6,10 +6,7 @@
6
6
  "rootDir": "../",
7
7
  "composite": true,
8
8
  "noCheck": true,
9
- // resolveJsonModule must remain false: this package contains LFS-tracked JSON data files
10
- // that appear as pointer text (not valid JSON) in CI environments where LFS is skipped.
11
- // Enabling resolveJsonModule would cause TS to attempt parsing these files and fail.
12
- "resolveJsonModule": false
9
+ "resolveJsonModule": true
13
10
  },
14
11
  "include": [
15
12
  "../src/**/*.ts",
@@ -6,10 +6,7 @@
6
6
  "rootDir": "../",
7
7
  "composite": true,
8
8
  "noCheck": true,
9
- // resolveJsonModule must remain false: this package contains LFS-tracked JSON data files
10
- // that appear as pointer text (not valid JSON) in CI environments where LFS is skipped.
11
- // Enabling resolveJsonModule would cause TS to attempt parsing these files and fail.
12
- "resolveJsonModule": false
9
+ "resolveJsonModule": true
13
10
  },
14
11
  "include": [
15
12
  "../src/**/*.ts",
@@ -2,12 +2,7 @@
2
2
  "extends": "../tsconfig",
3
3
  "compilerOptions": {
4
4
  "paths": {},
5
- // resolveJsonModule must remain false: this package contains LFS-tracked JSON data files
6
- // that appear as pointer text (not valid JSON) in CI environments where LFS is skipped.
7
- // We also intentionally do not set "target": "es5" here (unlike the standard generated
8
- // build tsconfig) because the ESLint require-unicode-regexp rule enforces the 'u' flag
9
- // on regexes, which requires es6+ target to compile.
10
- "resolveJsonModule": false
5
+ "resolveJsonModule": true
11
6
  },
12
7
  "include": ["../src/**/*.ts", "../src/**/*.tsx"],
13
8
  "exclude": [
@@ -214,14 +214,16 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
214
214
  return;
215
215
  }
216
216
  var lastChar = newText[newText.length - 1];
217
- if (!/[\t-\r !,\.:;\?\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/.test(lastChar)) {
217
+ // eslint-disable-next-line require-unicode-regexp
218
+ if (!/[\s.,;:!?]/.test(lastChar)) {
218
219
  return;
219
220
  }
220
221
 
221
222
  // Only fire if the previous state did not already end on a boundary,
222
223
  // so we don't double-count when multiple boundary chars are inserted.
223
224
  var prevLastChar = prevText[prevText.length - 1];
224
- if (prevLastChar && /[\t-\r !,\.:;\?\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/.test(prevLastChar)) {
225
+ // eslint-disable-next-line require-unicode-regexp
226
+ if (prevLastChar && /[\s.,;:!?]/.test(prevLastChar)) {
225
227
  return;
226
228
  }
227
229
  var beforeBoundary = newText.slice(0, -1).trimEnd();
@@ -299,7 +301,9 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
299
301
  },
300
302
  focus: function focus() {
301
303
  (0, _textPredictor.loadDefaultVocabulary)();
302
- (0, _textPredictor.loadVectorsAsync)().catch(function () {});
304
+ (0, _textPredictor.loadVectorsAsync)({
305
+ getBinaryUrl: options === null || options === void 0 ? void 0 : options.getVectorsBinaryUrl
306
+ }).catch(function () {});
303
307
  if (!hasIngestedPage) {
304
308
  hasIngestedPage = true;
305
309
  if (options !== null && options !== void 0 && options.getContext) {
@@ -16,9 +16,7 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
16
16
  *
17
17
  * Operates synchronously on pre-loaded data. Each stage gracefully degrades
18
18
  * when its required data isn't available (cold → warm → full warm).
19
- */ // resolveJsonModule is disabled for this package (see tsconfig.json) to prevent
20
- // TypeScript from parsing LFS pointer files during CI typecheck. JSON imports
21
- // are typed via the '*.json' declaration in typings.d.ts.
19
+ */
22
20
  // ─── Types ──────────────────────────────────────────────────
23
21
 
24
22
  /** Metadata returned by the grammar filter for debug logging in the caller. */
@@ -25,7 +25,8 @@ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/
25
25
 
26
26
  // ─── Constants ───────────────────────────────────────────────────────────────
27
27
 
28
- var WORD_BOUNDARY_CHARS = /[\t-\r !,\.:;\?\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/;
28
+ // eslint-disable-next-line require-unicode-regexp
29
+ var WORD_BOUNDARY_CHARS = /[\s.,;:!?]/;
29
30
  var DEFAULT_DEBOUNCE_MS = 300;
30
31
 
31
32
  /**
@@ -75,6 +76,7 @@ var createSlowLaneClient = exports.createSlowLaneClient = function createSlowLan
75
76
  }
76
77
  return _context.abrupt("return");
77
78
  case 2:
79
+ // eslint-disable-next-line require-unicode-regexp
78
80
  url = "".concat(baseUrl.replace(/\/$/, '')).concat(endpoint);
79
81
  payload = {
80
82
  text: text,
@@ -37,7 +37,8 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
37
37
  // import { rankCandidates, isGrammarAllowed } from './scoring-pipeline';
38
38
  // ─── Constants ───────────────────────────────────────────────────────────────
39
39
 
40
- var PUNCTUATION_BOUNDARY_REGEX = /^[!"'-\),\.:;\?\[\]`\{\}]+|[!"'-\),\.:;\?\[\]`\{\}]+$/g;
40
+ // eslint-disable-next-line require-unicode-regexp
41
+ var PUNCTUATION_BOUNDARY_REGEX = /^[.,;:!?()\[\]{}"'`]+|[.,;:!?()\[\]{}"'`]+$/g;
41
42
  var MIN_PREFIX_LENGTH = 3;
42
43
  var MAX_CANDIDATES = 200;
43
44
  var CONTEXT_WORDS = 10;
@@ -311,8 +312,8 @@ var getContextVectorForScoring = function getContextVectorForScoring(textBefore)
311
312
  };
312
313
  var tokenize = function tokenize(text) {
313
314
  var tokens = [];
314
- // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
315
- var _iterator7 = _createForOfIteratorHelper(text.toLowerCase().split(/[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+/)),
315
+ // eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace
316
+ var _iterator7 = _createForOfIteratorHelper(text.toLowerCase().split(/\s+/)),
316
317
  _step7;
317
318
  try {
318
319
  for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
@@ -332,13 +333,15 @@ var tokenize = function tokenize(text) {
332
333
  };
333
334
  var extractPreviousWord = function extractPreviousWord(text) {
334
335
  // 1. Split the text by newlines or punctuation (. ? !)
335
- var sentences = text.split(/[\n!\.\?]+/);
336
+ // eslint-disable-next-line require-unicode-regexp
337
+ var sentences = text.split(/[\n.?!]+/);
336
338
 
337
339
  // 2. Only look at the current sentence/line the user is typing in
338
340
  var currentSentence = sentences[sentences.length - 1];
339
341
 
340
342
  // 3. Extract the previous word as normal
341
- var words = currentSentence.trimEnd().split(/[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+/);
343
+ // eslint-disable-next-line require-unicode-regexp
344
+ var words = currentSentence.trimEnd().split(/\s+/);
342
345
  return words.length >= 2 ? words[words.length - 2] : '';
343
346
  };
344
347
 
@@ -489,7 +492,8 @@ var predict = exports.predict = function predict(textBefore) {
489
492
  // }
490
493
 
491
494
  // ── Step 2: Prefix completion (≥3 chars typed) ──────────────────────────
492
- if (textBefore.length > 0 && /[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]$/.test(textBefore)) {
495
+ // eslint-disable-next-line require-unicode-regexp
496
+ if (textBefore.length > 0 && /\s$/.test(textBefore)) {
493
497
  return null;
494
498
  }
495
499
  var trimmed = textBefore.trimEnd();
@@ -689,10 +693,8 @@ var predict = exports.predict = function predict(textBefore) {
689
693
 
690
694
  // ─── Data Loading ────────────────────────────────────────────────────────────
691
695
 
692
- var DEFAULT_VECTORS_URL = '/data/word-vectors_10k.bin';
693
696
  var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
694
697
  var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(options) {
695
- var _options$vectorsUrl;
696
698
  var url, res, buffer, float32, wordIndex, nWords, dim;
697
699
  return _regenerator.default.wrap(function _callee$(_context) {
698
700
  while (1) switch (_context.prev = _context.next) {
@@ -703,25 +705,47 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
703
705
  }
704
706
  return _context.abrupt("return");
705
707
  case 2:
708
+ if (options !== null && options !== void 0 && options.getBinaryUrl) {
709
+ _context.next = 5;
710
+ break;
711
+ }
712
+ // eslint-disable-next-line no-console
713
+ console.warn('[text-predictor] loadVectorsAsync called without a getBinaryUrl — vectors will not load. Pass getVectorsBinaryUrl via plugin options.');
714
+ return _context.abrupt("return");
715
+ case 5:
706
716
  vectorsLoadStarted = true;
707
- url = (_options$vectorsUrl = options === null || options === void 0 ? void 0 : options.vectorsUrl) !== null && _options$vectorsUrl !== void 0 ? _options$vectorsUrl : DEFAULT_VECTORS_URL;
708
- _context.prev = 4;
709
- _context.next = 7;
717
+ _context.prev = 6;
718
+ _context.next = 9;
719
+ return options.getBinaryUrl();
720
+ case 9:
721
+ url = _context.sent;
722
+ _context.next = 17;
723
+ break;
724
+ case 12:
725
+ _context.prev = 12;
726
+ _context.t0 = _context["catch"](6);
727
+ vectorsLoadStarted = false;
728
+ // eslint-disable-next-line no-console
729
+ console.warn('[text-predictor] Failed to resolve vectors URL:', _context.t0);
730
+ return _context.abrupt("return");
731
+ case 17:
732
+ _context.prev = 17;
733
+ _context.next = 20;
710
734
  return fetch(url);
711
- case 7:
735
+ case 20:
712
736
  res = _context.sent;
713
737
  if (res.ok) {
714
- _context.next = 12;
738
+ _context.next = 25;
715
739
  break;
716
740
  }
717
741
  vectorsLoadStarted = false;
718
742
  // eslint-disable-next-line no-console
719
743
  console.warn("[text-predictor] Failed to load vectors: ".concat(res.status));
720
744
  return _context.abrupt("return");
721
- case 12:
722
- _context.next = 14;
745
+ case 25:
746
+ _context.next = 27;
723
747
  return res.arrayBuffer();
724
- case 14:
748
+ case 27:
725
749
  buffer = _context.sent;
726
750
  float32 = new Float32Array(buffer);
727
751
  wordIndex = _word_index_10k.default;
@@ -741,19 +765,19 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
741
765
  sizeBytes: float32.byteLength
742
766
  });
743
767
  }
744
- _context.next = 28;
768
+ _context.next = 41;
745
769
  break;
746
- case 24:
747
- _context.prev = 24;
748
- _context.t0 = _context["catch"](4);
770
+ case 37:
771
+ _context.prev = 37;
772
+ _context.t1 = _context["catch"](17);
749
773
  vectorsLoadStarted = false;
750
774
  // eslint-disable-next-line no-console
751
- console.warn('[text-predictor] Failed to load vectors:', _context.t0);
752
- case 28:
775
+ console.warn('[text-predictor] Failed to load vectors:', _context.t1);
776
+ case 41:
753
777
  case "end":
754
778
  return _context.stop();
755
779
  }
756
- }, _callee, null, [[4, 24]]);
780
+ }, _callee, null, [[6, 12], [17, 37]]);
757
781
  }));
758
782
  return function loadVectorsAsync(_x) {
759
783
  return _ref4.apply(this, arguments);
@@ -212,14 +212,16 @@ export const createAutocompletePlugin = options => {
212
212
  return;
213
213
  }
214
214
  const lastChar = newText[newText.length - 1];
215
- if (!/[\s.,;:!?]/u.test(lastChar)) {
215
+ // eslint-disable-next-line require-unicode-regexp
216
+ if (!/[\s.,;:!?]/.test(lastChar)) {
216
217
  return;
217
218
  }
218
219
 
219
220
  // Only fire if the previous state did not already end on a boundary,
220
221
  // so we don't double-count when multiple boundary chars are inserted.
221
222
  const prevLastChar = prevText[prevText.length - 1];
222
- if (prevLastChar && /[\s.,;:!?]/u.test(prevLastChar)) {
223
+ // eslint-disable-next-line require-unicode-regexp
224
+ if (prevLastChar && /[\s.,;:!?]/.test(prevLastChar)) {
223
225
  return;
224
226
  }
225
227
  const beforeBoundary = newText.slice(0, -1).trimEnd();
@@ -300,7 +302,9 @@ export const createAutocompletePlugin = options => {
300
302
  },
301
303
  focus: () => {
302
304
  loadDefaultVocabulary();
303
- loadVectorsAsync().catch(() => {});
305
+ loadVectorsAsync({
306
+ getBinaryUrl: options === null || options === void 0 ? void 0 : options.getVectorsBinaryUrl
307
+ }).catch(() => {});
304
308
  if (!hasIngestedPage) {
305
309
  hasIngestedPage = true;
306
310
  if (options !== null && options !== void 0 && options.getContext) {
@@ -5,9 +5,6 @@
5
5
  * when its required data isn't available (cold → warm → full warm).
6
6
  */
7
7
 
8
- // resolveJsonModule is disabled for this package (see tsconfig.json) to prevent
9
- // TypeScript from parsing LFS pointer files during CI typecheck. JSON imports
10
- // are typed via the '*.json' declaration in typings.d.ts.
11
8
  import posTagsData from './data/combined_l2_l3_pos_tags.json';
12
9
  import ghostPosTagsData from './data/ghost_pos_tags.json';
13
10
  import grammarTransitionsData from './data/grammar_transitions_10k.json';
@@ -15,7 +15,8 @@
15
15
 
16
16
  // ─── Constants ───────────────────────────────────────────────────────────────
17
17
 
18
- const WORD_BOUNDARY_CHARS = /[\s.,;:!?]/u;
18
+ // eslint-disable-next-line require-unicode-regexp
19
+ const WORD_BOUNDARY_CHARS = /[\s.,;:!?]/;
19
20
  const DEFAULT_DEBOUNCE_MS = 300;
20
21
 
21
22
  /**
@@ -55,7 +56,9 @@ export const createSlowLaneClient = config => {
55
56
  if (!text || text.trim().length === 0) {
56
57
  return;
57
58
  }
58
- const url = `${baseUrl.replace(/\/$/u, '')}${endpoint}`;
59
+
60
+ // eslint-disable-next-line require-unicode-regexp
61
+ const url = `${baseUrl.replace(/\/$/, '')}${endpoint}`;
59
62
  const payload = {
60
63
  text,
61
64
  session_id: sessionId
@@ -26,7 +26,8 @@ import { getStoredContextVector, getStoredLmLogits } from './slow-lane-client';
26
26
 
27
27
  // ─── Constants ───────────────────────────────────────────────────────────────
28
28
 
29
- const PUNCTUATION_BOUNDARY_REGEX = /^[.,;:!?()\[\]{}"'`]+|[.,;:!?()\[\]{}"'`]+$/gu;
29
+ // eslint-disable-next-line require-unicode-regexp
30
+ const PUNCTUATION_BOUNDARY_REGEX = /^[.,;:!?()\[\]{}"'`]+|[.,;:!?()\[\]{}"'`]+$/g;
30
31
  const MIN_PREFIX_LENGTH = 3;
31
32
  const MAX_CANDIDATES = 200;
32
33
  const CONTEXT_WORDS = 10;
@@ -236,8 +237,8 @@ const getContextVectorForScoring = textBefore => {
236
237
  };
237
238
  const tokenize = text => {
238
239
  const tokens = [];
239
- // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
240
- for (const raw of text.toLowerCase().split(/\s+/u)) {
240
+ // eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace
241
+ for (const raw of text.toLowerCase().split(/\s+/)) {
241
242
  // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
242
243
  const clean = raw.replace(PUNCTUATION_BOUNDARY_REGEX, '');
243
244
  if (clean.length >= 2) {
@@ -248,13 +249,15 @@ const tokenize = text => {
248
249
  };
249
250
  const extractPreviousWord = text => {
250
251
  // 1. Split the text by newlines or punctuation (. ? !)
251
- const sentences = text.split(/[\n.?!]+/u);
252
+ // eslint-disable-next-line require-unicode-regexp
253
+ const sentences = text.split(/[\n.?!]+/);
252
254
 
253
255
  // 2. Only look at the current sentence/line the user is typing in
254
256
  const currentSentence = sentences[sentences.length - 1];
255
257
 
256
258
  // 3. Extract the previous word as normal
257
- const words = currentSentence.trimEnd().split(/\s+/u);
259
+ // eslint-disable-next-line require-unicode-regexp
260
+ const words = currentSentence.trimEnd().split(/\s+/);
258
261
  return words.length >= 2 ? words[words.length - 2] : '';
259
262
  };
260
263
 
@@ -387,7 +390,8 @@ export const predict = textBefore => {
387
390
  // }
388
391
 
389
392
  // ── Step 2: Prefix completion (≥3 chars typed) ──────────────────────────
390
- if (textBefore.length > 0 && /\s$/u.test(textBefore)) {
393
+ // eslint-disable-next-line require-unicode-regexp
394
+ if (textBefore.length > 0 && /\s$/.test(textBefore)) {
391
395
  return null;
392
396
  }
393
397
  const trimmed = textBefore.trimEnd();
@@ -561,14 +565,25 @@ export const predict = textBefore => {
561
565
 
562
566
  // ─── Data Loading ────────────────────────────────────────────────────────────
563
567
 
564
- const DEFAULT_VECTORS_URL = '/data/word-vectors_10k.bin';
565
568
  export const loadVectorsAsync = async options => {
566
- var _options$vectorsUrl;
567
569
  if (vectorStore || vectorsLoadStarted) {
568
570
  return;
569
571
  }
572
+ if (!(options !== null && options !== void 0 && options.getBinaryUrl)) {
573
+ // eslint-disable-next-line no-console
574
+ console.warn('[text-predictor] loadVectorsAsync called without a getBinaryUrl — vectors will not load. Pass getVectorsBinaryUrl via plugin options.');
575
+ return;
576
+ }
570
577
  vectorsLoadStarted = true;
571
- const url = (_options$vectorsUrl = options === null || options === void 0 ? void 0 : options.vectorsUrl) !== null && _options$vectorsUrl !== void 0 ? _options$vectorsUrl : DEFAULT_VECTORS_URL;
578
+ let url;
579
+ try {
580
+ url = await options.getBinaryUrl();
581
+ } catch (e) {
582
+ vectorsLoadStarted = false;
583
+ // eslint-disable-next-line no-console
584
+ console.warn('[text-predictor] Failed to resolve vectors URL:', e);
585
+ return;
586
+ }
572
587
  try {
573
588
  const res = await fetch(url);
574
589
  if (!res.ok) {
@@ -207,14 +207,16 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options)
207
207
  return;
208
208
  }
209
209
  var lastChar = newText[newText.length - 1];
210
- if (!/[\t-\r !,\.:;\?\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/.test(lastChar)) {
210
+ // eslint-disable-next-line require-unicode-regexp
211
+ if (!/[\s.,;:!?]/.test(lastChar)) {
211
212
  return;
212
213
  }
213
214
 
214
215
  // Only fire if the previous state did not already end on a boundary,
215
216
  // so we don't double-count when multiple boundary chars are inserted.
216
217
  var prevLastChar = prevText[prevText.length - 1];
217
- if (prevLastChar && /[\t-\r !,\.:;\?\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/.test(prevLastChar)) {
218
+ // eslint-disable-next-line require-unicode-regexp
219
+ if (prevLastChar && /[\s.,;:!?]/.test(prevLastChar)) {
218
220
  return;
219
221
  }
220
222
  var beforeBoundary = newText.slice(0, -1).trimEnd();
@@ -292,7 +294,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options)
292
294
  },
293
295
  focus: function focus() {
294
296
  loadDefaultVocabulary();
295
- loadVectorsAsync().catch(function () {});
297
+ loadVectorsAsync({
298
+ getBinaryUrl: options === null || options === void 0 ? void 0 : options.getVectorsBinaryUrl
299
+ }).catch(function () {});
296
300
  if (!hasIngestedPage) {
297
301
  hasIngestedPage = true;
298
302
  if (options !== null && options !== void 0 && options.getContext) {
@@ -9,9 +9,6 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
9
9
  * when its required data isn't available (cold → warm → full warm).
10
10
  */
11
11
 
12
- // resolveJsonModule is disabled for this package (see tsconfig.json) to prevent
13
- // TypeScript from parsing LFS pointer files during CI typecheck. JSON imports
14
- // are typed via the '*.json' declaration in typings.d.ts.
15
12
  import posTagsData from './data/combined_l2_l3_pos_tags.json';
16
13
  import ghostPosTagsData from './data/ghost_pos_tags.json';
17
14
  import grammarTransitionsData from './data/grammar_transitions_10k.json';
@@ -18,7 +18,8 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
18
18
 
19
19
  // ─── Constants ───────────────────────────────────────────────────────────────
20
20
 
21
- var WORD_BOUNDARY_CHARS = /[\t-\r !,\.:;\?\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/;
21
+ // eslint-disable-next-line require-unicode-regexp
22
+ var WORD_BOUNDARY_CHARS = /[\s.,;:!?]/;
22
23
  var DEFAULT_DEBOUNCE_MS = 300;
23
24
 
24
25
  /**
@@ -68,6 +69,7 @@ export var createSlowLaneClient = function createSlowLaneClient(config) {
68
69
  }
69
70
  return _context.abrupt("return");
70
71
  case 2:
72
+ // eslint-disable-next-line require-unicode-regexp
71
73
  url = "".concat(baseUrl.replace(/\/$/, '')).concat(endpoint);
72
74
  payload = {
73
75
  text: text,
@@ -34,7 +34,8 @@ import { getStoredContextVector, getStoredLmLogits } from './slow-lane-client';
34
34
 
35
35
  // ─── Constants ───────────────────────────────────────────────────────────────
36
36
 
37
- var PUNCTUATION_BOUNDARY_REGEX = /^[!"'-\),\.:;\?\[\]`\{\}]+|[!"'-\),\.:;\?\[\]`\{\}]+$/g;
37
+ // eslint-disable-next-line require-unicode-regexp
38
+ var PUNCTUATION_BOUNDARY_REGEX = /^[.,;:!?()\[\]{}"'`]+|[.,;:!?()\[\]{}"'`]+$/g;
38
39
  var MIN_PREFIX_LENGTH = 3;
39
40
  var MAX_CANDIDATES = 200;
40
41
  var CONTEXT_WORDS = 10;
@@ -308,8 +309,8 @@ var getContextVectorForScoring = function getContextVectorForScoring(textBefore)
308
309
  };
309
310
  var tokenize = function tokenize(text) {
310
311
  var tokens = [];
311
- // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
312
- var _iterator7 = _createForOfIteratorHelper(text.toLowerCase().split(/[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+/)),
312
+ // eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace
313
+ var _iterator7 = _createForOfIteratorHelper(text.toLowerCase().split(/\s+/)),
313
314
  _step7;
314
315
  try {
315
316
  for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
@@ -329,13 +330,15 @@ var tokenize = function tokenize(text) {
329
330
  };
330
331
  var extractPreviousWord = function extractPreviousWord(text) {
331
332
  // 1. Split the text by newlines or punctuation (. ? !)
332
- var sentences = text.split(/[\n!\.\?]+/);
333
+ // eslint-disable-next-line require-unicode-regexp
334
+ var sentences = text.split(/[\n.?!]+/);
333
335
 
334
336
  // 2. Only look at the current sentence/line the user is typing in
335
337
  var currentSentence = sentences[sentences.length - 1];
336
338
 
337
339
  // 3. Extract the previous word as normal
338
- var words = currentSentence.trimEnd().split(/[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+/);
340
+ // eslint-disable-next-line require-unicode-regexp
341
+ var words = currentSentence.trimEnd().split(/\s+/);
339
342
  return words.length >= 2 ? words[words.length - 2] : '';
340
343
  };
341
344
 
@@ -486,7 +489,8 @@ export var predict = function predict(textBefore) {
486
489
  // }
487
490
 
488
491
  // ── Step 2: Prefix completion (≥3 chars typed) ──────────────────────────
489
- if (textBefore.length > 0 && /[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]$/.test(textBefore)) {
492
+ // eslint-disable-next-line require-unicode-regexp
493
+ if (textBefore.length > 0 && /\s$/.test(textBefore)) {
490
494
  return null;
491
495
  }
492
496
  var trimmed = textBefore.trimEnd();
@@ -686,10 +690,8 @@ export var predict = function predict(textBefore) {
686
690
 
687
691
  // ─── Data Loading ────────────────────────────────────────────────────────────
688
692
 
689
- var DEFAULT_VECTORS_URL = '/data/word-vectors_10k.bin';
690
693
  export var loadVectorsAsync = /*#__PURE__*/function () {
691
694
  var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(options) {
692
- var _options$vectorsUrl;
693
695
  var url, res, buffer, float32, wordIndex, nWords, dim;
694
696
  return _regeneratorRuntime.wrap(function _callee$(_context) {
695
697
  while (1) switch (_context.prev = _context.next) {
@@ -700,25 +702,47 @@ export var loadVectorsAsync = /*#__PURE__*/function () {
700
702
  }
701
703
  return _context.abrupt("return");
702
704
  case 2:
705
+ if (options !== null && options !== void 0 && options.getBinaryUrl) {
706
+ _context.next = 5;
707
+ break;
708
+ }
709
+ // eslint-disable-next-line no-console
710
+ console.warn('[text-predictor] loadVectorsAsync called without a getBinaryUrl — vectors will not load. Pass getVectorsBinaryUrl via plugin options.');
711
+ return _context.abrupt("return");
712
+ case 5:
703
713
  vectorsLoadStarted = true;
704
- url = (_options$vectorsUrl = options === null || options === void 0 ? void 0 : options.vectorsUrl) !== null && _options$vectorsUrl !== void 0 ? _options$vectorsUrl : DEFAULT_VECTORS_URL;
705
- _context.prev = 4;
706
- _context.next = 7;
714
+ _context.prev = 6;
715
+ _context.next = 9;
716
+ return options.getBinaryUrl();
717
+ case 9:
718
+ url = _context.sent;
719
+ _context.next = 17;
720
+ break;
721
+ case 12:
722
+ _context.prev = 12;
723
+ _context.t0 = _context["catch"](6);
724
+ vectorsLoadStarted = false;
725
+ // eslint-disable-next-line no-console
726
+ console.warn('[text-predictor] Failed to resolve vectors URL:', _context.t0);
727
+ return _context.abrupt("return");
728
+ case 17:
729
+ _context.prev = 17;
730
+ _context.next = 20;
707
731
  return fetch(url);
708
- case 7:
732
+ case 20:
709
733
  res = _context.sent;
710
734
  if (res.ok) {
711
- _context.next = 12;
735
+ _context.next = 25;
712
736
  break;
713
737
  }
714
738
  vectorsLoadStarted = false;
715
739
  // eslint-disable-next-line no-console
716
740
  console.warn("[text-predictor] Failed to load vectors: ".concat(res.status));
717
741
  return _context.abrupt("return");
718
- case 12:
719
- _context.next = 14;
742
+ case 25:
743
+ _context.next = 27;
720
744
  return res.arrayBuffer();
721
- case 14:
745
+ case 27:
722
746
  buffer = _context.sent;
723
747
  float32 = new Float32Array(buffer);
724
748
  wordIndex = wordIndexData;
@@ -738,19 +762,19 @@ export var loadVectorsAsync = /*#__PURE__*/function () {
738
762
  sizeBytes: float32.byteLength
739
763
  });
740
764
  }
741
- _context.next = 28;
765
+ _context.next = 41;
742
766
  break;
743
- case 24:
744
- _context.prev = 24;
745
- _context.t0 = _context["catch"](4);
767
+ case 37:
768
+ _context.prev = 37;
769
+ _context.t1 = _context["catch"](17);
746
770
  vectorsLoadStarted = false;
747
771
  // eslint-disable-next-line no-console
748
- console.warn('[text-predictor] Failed to load vectors:', _context.t0);
749
- case 28:
772
+ console.warn('[text-predictor] Failed to load vectors:', _context.t1);
773
+ case 41:
750
774
  case "end":
751
775
  return _context.stop();
752
776
  }
753
- }, _callee, null, [[4, 24]]);
777
+ }, _callee, null, [[6, 12], [17, 37]]);
754
778
  }));
755
779
  return function loadVectorsAsync(_x) {
756
780
  return _ref4.apply(this, arguments);
@@ -32,5 +32,11 @@ export interface AutocompletePluginOptions {
32
32
  * word-frequency boosting. Called lazily so the preset can remain synchronous.
33
33
  */
34
34
  getContext?: () => Promise<AutocompleteContext | undefined>;
35
+ /**
36
+ * Async function that resolves to a URL for the word vectors binary file.
37
+ * When provided, this takes precedence over the bundled asset URL.
38
+ * Use this to serve vectors from a CDN or media service in production.
39
+ */
40
+ getVectorsBinaryUrl?: () => Promise<string>;
35
41
  }
36
42
  export declare const createAutocompletePlugin: (options?: AutocompletePluginOptions) => SafePlugin<AutocompletePluginState>;
@@ -83,7 +83,7 @@ export declare const incrementSessionFreq: (word: string) => void;
83
83
  export declare const ingestDocumentPage: (pageContent: string | undefined) => void;
84
84
  export declare const predict: (textBefore: string) => string | null;
85
85
  export declare const loadVectorsAsync: (options?: {
86
- vectorsUrl?: string;
86
+ getBinaryUrl?: () => Promise<string>;
87
87
  }) => Promise<void>;
88
88
  export declare const initVectors: (store: VectorStore) => void;
89
89
  export declare const loadDefaultVocabulary: () => void;