@atlaskit/editor-plugin-autocomplete 8.0.0 → 9.1.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 (79) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/dist/cjs/analytics/ufo.js +2 -1
  3. package/dist/cjs/pm-plugins/artifact-loader.js +135 -0
  4. package/dist/cjs/pm-plugins/artifacts-manifest.js +3 -0
  5. package/dist/cjs/pm-plugins/autocomplete-plugin.js +498 -160
  6. package/dist/cjs/pm-plugins/canonical-lm-scoring.js +371 -0
  7. package/dist/cjs/pm-plugins/debug-mode.js +105 -7
  8. package/dist/cjs/pm-plugins/ghost-text-decoration.js +12 -3
  9. package/dist/cjs/pm-plugins/local-slow-lane-client.js +1202 -453
  10. package/dist/cjs/pm-plugins/scoring-pipeline.js +164 -19
  11. package/dist/cjs/pm-plugins/slow-lane-client.js +100 -37
  12. package/dist/cjs/pm-plugins/text-predictor.js +2099 -309
  13. package/dist/es2019/analytics/ufo.js +2 -0
  14. package/dist/es2019/pm-plugins/artifact-loader.js +70 -0
  15. package/dist/es2019/pm-plugins/artifacts-manifest.js +3 -0
  16. package/dist/es2019/pm-plugins/autocomplete-plugin.js +489 -152
  17. package/dist/es2019/pm-plugins/canonical-lm-scoring.js +270 -0
  18. package/dist/es2019/pm-plugins/debug-mode.js +102 -7
  19. package/dist/es2019/pm-plugins/ghost-text-decoration.js +12 -3
  20. package/dist/es2019/pm-plugins/local-slow-lane-client.js +940 -326
  21. package/dist/es2019/pm-plugins/scoring-pipeline.js +137 -13
  22. package/dist/es2019/pm-plugins/slow-lane-client.js +94 -37
  23. package/dist/es2019/pm-plugins/text-predictor.js +1692 -200
  24. package/dist/esm/analytics/ufo.js +2 -1
  25. package/dist/esm/pm-plugins/artifact-loader.js +128 -0
  26. package/dist/esm/pm-plugins/artifacts-manifest.js +3 -0
  27. package/dist/esm/pm-plugins/autocomplete-plugin.js +501 -163
  28. package/dist/esm/pm-plugins/canonical-lm-scoring.js +364 -0
  29. package/dist/esm/pm-plugins/debug-mode.js +104 -6
  30. package/dist/esm/pm-plugins/ghost-text-decoration.js +12 -3
  31. package/dist/esm/pm-plugins/local-slow-lane-client.js +1200 -451
  32. package/dist/esm/pm-plugins/scoring-pipeline.js +163 -18
  33. package/dist/esm/pm-plugins/slow-lane-client.js +100 -37
  34. package/dist/esm/pm-plugins/text-predictor.js +2101 -313
  35. package/dist/types/analytics/ufo.d.ts +1 -0
  36. package/dist/types/pm-plugins/artifact-loader.d.ts +44 -0
  37. package/dist/types/pm-plugins/artifacts-manifest.d.ts +3 -0
  38. package/dist/types/pm-plugins/autocomplete-plugin.d.ts +39 -0
  39. package/dist/types/pm-plugins/canonical-lm-scoring.d.ts +158 -0
  40. package/dist/types/pm-plugins/debug-mode.d.ts +55 -1
  41. package/dist/types/pm-plugins/ghost-text-decoration.d.ts +5 -2
  42. package/dist/types/pm-plugins/local-slow-lane-client.d.ts +87 -54
  43. package/dist/types/pm-plugins/scoring-pipeline.d.ts +33 -1
  44. package/dist/types/pm-plugins/slow-lane-client.d.ts +51 -3
  45. package/dist/types/pm-plugins/text-predictor.d.ts +156 -3
  46. package/package.json +2 -2
  47. package/src/analytics/ufo.ts +5 -0
  48. package/src/pm-plugins/artifact-loader.ts +92 -0
  49. package/src/pm-plugins/artifacts-manifest.ts +3 -0
  50. package/src/pm-plugins/autocomplete-plugin.ts +584 -154
  51. package/src/pm-plugins/canonical-lm-scoring.ts +412 -0
  52. package/src/pm-plugins/debug-mode.ts +134 -7
  53. package/src/pm-plugins/ghost-text-decoration.ts +13 -4
  54. package/src/pm-plugins/local-slow-lane-client.ts +1302 -386
  55. package/src/pm-plugins/scoring-pipeline.ts +167 -17
  56. package/src/pm-plugins/slow-lane-client.ts +147 -62
  57. package/src/pm-plugins/text-predictor.ts +2119 -254
  58. package/CANONICAL_FIX__DO_NOT_USE_ME_A/package.json +0 -8
  59. package/CANONICAL_FIX__DO_NOT_USE_ME_B/package.json +0 -8
  60. package/CANONICAL_FIX__DO_NOT_USE_ME_C/package.json +0 -8
  61. package/dist/cjs/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -18
  62. package/dist/cjs/entry-points/src-pm-plugins-slow-lane-client.js +0 -36
  63. package/dist/cjs/entry-points/src-pm-plugins-text-predictor.js +0 -66
  64. package/dist/es2019/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -2
  65. package/dist/es2019/entry-points/src-pm-plugins-slow-lane-client.js +0 -2
  66. package/dist/es2019/entry-points/src-pm-plugins-text-predictor.js +0 -2
  67. package/dist/esm/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -2
  68. package/dist/esm/entry-points/src-pm-plugins-slow-lane-client.js +0 -2
  69. package/dist/esm/entry-points/src-pm-plugins-text-predictor.js +0 -2
  70. package/dist/types/entry-points/src-pm-plugins-autocomplete-plugin.d.ts +0 -2
  71. package/dist/types/entry-points/src-pm-plugins-slow-lane-client.d.ts +0 -2
  72. package/dist/types/entry-points/src-pm-plugins-text-predictor.d.ts +0 -2
  73. package/scripts/gen_first_token_to_words.py +0 -170
  74. package/src/entry-points/src-pm-plugins-autocomplete-plugin.ts +0 -7
  75. package/src/entry-points/src-pm-plugins-slow-lane-client.ts +0 -13
  76. package/src/entry-points/src-pm-plugins-text-predictor.ts +0 -14
  77. package/src/pm-plugins/autocomplete-plugin/package.json +0 -8
  78. package/src/pm-plugins/slow-lane-client/package.json +0 -8
  79. package/src/pm-plugins/text-predictor/package.json +0 -8
@@ -1,5 +1,5 @@
1
- import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
3
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
4
4
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
5
5
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
@@ -11,14 +11,15 @@ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
11
11
  import { keydownHandler } from '@atlaskit/editor-prosemirror/keymap';
12
12
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
13
13
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
14
- import { isAutocompleteDebugEnabled } from './debug-mode';
14
+ import { CTC_STYLES, ctcTag, isAutocompleteDebugEnabled, isAutocompleteDebugVerbose } from './debug-mode';
15
15
  import { createGhostTextDecorationSet } from './ghost-text-decoration';
16
16
  import { createLocalSlowLaneClient } from './local-slow-lane-client';
17
17
  import { loadGrammarDataAsync } from './scoring-pipeline';
18
- import { createSlowLaneClient, setDefaultSlowLaneClient, isWordBoundary } from './slow-lane-client';
19
- import { predict, loadDefaultVocabulary, loadVectorsAsync, incrementSessionFreq, ingestDocumentPage } from './text-predictor';
18
+ import { clearDefaultSlowLaneClient, createSlowLaneClient, getDefaultSlowLaneClientStatus, isWordBoundary, setDefaultSlowLaneClient } from './slow-lane-client';
19
+ import { predict, loadDefaultVocabulary, loadVectorsAsync, incrementSessionFreq, ingestDocumentPage, getLastPredictionDebug, noteSuggestionAccepted } from './text-predictor';
20
20
  export var autocompletePluginKey = new PluginKey('autocomplete');
21
- var DEBOUNCE_MS = 150;
21
+ var PREDICTION_COALESCE_MS = 0;
22
+ var GHOST_DECISION_BUDGET_MS = 100;
22
23
  var NETWORK_SLOW_LANE_DEBOUNCE_MS = 300;
23
24
  var LOCAL_SLOW_LANE_DEBOUNCE_MS = 100;
24
25
  var CONTEXT_REFRESH_THROTTLE_MS = 1000;
@@ -33,11 +34,11 @@ var MAX_INGESTED_CONTEXT_TEXTS = 50;
33
34
  // on every word boundary for the plugin's lifetime.
34
35
  var MAX_CONTEXT_REFRESH_ATTEMPTS = 5;
35
36
  var INLINE_LEAF_TEXT = ' ';
37
+ var slowLaneClientInstanceSequence = 0;
36
38
 
37
- // eslint-disable-next-line require-unicode-regexp
38
- var TRAILING_NON_BOUNDARY_TOKEN_REGEX = /([^\s.,;:!?]*)$/;
39
39
  // eslint-disable-next-line require-unicode-regexp
40
40
  var WORD_BOUNDARY_CHARS_REGEX = /[\s.,;:!?]/;
41
+ var TRAILING_SURFACE_PREFIX_REGEX = /(?:['\x2D0-9A-Z_a-z\xAA\xB2\xB3\xB5\xB9\xBA\xBC-\xBE\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u0660-\u0669\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088F\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0966-\u096F\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09E6-\u09F1\u09F4-\u09F9\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AE6-\u0AEF\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71-\u0B77\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0BE6-\u0BF2\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5C\u0C5D\u0C60\u0C61\u0C66-\u0C6F\u0C78-\u0C7E\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDC-\u0CDE\u0CE0\u0CE1\u0CE6-\u0CEF\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D58-\u0D61\u0D66-\u0D78\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DE6-\u0DEF\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F20-\u0F33\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F-\u1049\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u1090-\u1099\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1369-\u137C\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A16\u1A20-\u1A54\u1A80-\u1A89\u1A90-\u1A99\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B50-\u1B59\u1B83-\u1BA0\u1BAE-\u1BE5\u1C00-\u1C23\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C8A\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2070\u2071\u2074-\u2079\u207F-\u2089\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2150-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2CFD\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u3192-\u3195\u31A0-\u31BF\u31F0-\u31FF\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7DC\uA7F1-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA830-\uA835\uA840-\uA873\uA882-\uA8B3\uA8D0-\uA8D9\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA900-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF-\uA9D9\uA9E0-\uA9E4\uA9E6-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDE80-\uDE9C\uDEA0-\uDED0\uDEE1-\uDEFB\uDF00-\uDF23\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDDC0-\uDDF3\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC58-\uDC76\uDC79-\uDC9E\uDCA7-\uDCAF\uDCE0-\uDCF2\uDCF4\uDCF5\uDCFB-\uDD1B\uDD20-\uDD39\uDD40-\uDD59\uDD80-\uDDB7\uDDBC-\uDDCF\uDDD2-\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE40-\uDE48\uDE60-\uDE7E\uDE80-\uDE9F\uDEC0-\uDEC7\uDEC9-\uDEE4\uDEEB-\uDEEF\uDF00-\uDF35\uDF40-\uDF55\uDF58-\uDF72\uDF78-\uDF91\uDFA9-\uDFAF]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDCFA-\uDD23\uDD30-\uDD39\uDD40-\uDD65\uDD6F-\uDD85\uDE60-\uDE7E\uDE80-\uDEA9\uDEB0\uDEB1\uDEC2-\uDEC7\uDF00-\uDF27\uDF30-\uDF45\uDF51-\uDF54\uDF70-\uDF81\uDFB0-\uDFCB\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC52-\uDC6F\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD03-\uDD26\uDD36-\uDD3F\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDD0-\uDDDA\uDDDC\uDDE1-\uDDF4\uDE00-\uDE11\uDE13-\uDE2B\uDE3F\uDE40\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDEF0-\uDEF9\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61\uDF80-\uDF89\uDF8B\uDF8E\uDF90-\uDFB5\uDFB7\uDFD1\uDFD3]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC50-\uDC59\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE50-\uDE59\uDE80-\uDEAA\uDEB8\uDEC0-\uDEC9\uDED0-\uDEE3\uDF00-\uDF1A\uDF30-\uDF3B\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCF2\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDD50-\uDD59\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8\uDFC0-\uDFE0\uDFF0-\uDFF9]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC50-\uDC6C\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD50-\uDD59\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDDA0-\uDDA9\uDDB0-\uDDDB\uDDE0-\uDDE9\uDEE0-\uDEF2\uDF02\uDF04-\uDF10\uDF12-\uDF33\uDF50-\uDF59\uDFB0\uDFC0-\uDFD4]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD80E\uD80F\uD81C-\uD822\uD840-\uD868\uD86A-\uD86D\uD86F-\uD872\uD874-\uD879\uD880-\uD883\uD885-\uD88C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2F\uDC41-\uDC46\uDC60-\uDFFF]|\uD810[\uDC00-\uDFFA]|\uD811[\uDC00-\uDE46]|\uD818[\uDD00-\uDD1D\uDD30-\uDD39]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDE70-\uDEBE\uDEC0-\uDEC9\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF50-\uDF59\uDF5B-\uDF61\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDD40-\uDD6C\uDD70-\uDD79\uDE40-\uDE96\uDEA0-\uDEB8\uDEBB-\uDED3\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3\uDFF2-\uDFF6]|\uD823[\uDC00-\uDCD5\uDCFF-\uDD1E\uDD80-\uDDF2]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD32\uDD50-\uDD52\uDD55\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD833[\uDCF0-\uDCF9]|\uD834[\uDEC0-\uDED3\uDEE0-\uDEF3\uDF60-\uDF78]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD837[\uDF00-\uDF1E\uDF25-\uDF2A]|\uD838[\uDC30-\uDC6D\uDD00-\uDD2C\uDD37-\uDD3D\uDD40-\uDD49\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB\uDEF0-\uDEF9]|\uD839[\uDCD0-\uDCEB\uDCF0-\uDCF9\uDDD0-\uDDED\uDDF0-\uDDFA\uDEC0-\uDEDE\uDEE0-\uDEE2\uDEE4\uDEE5\uDEE7-\uDEED\uDEF0-\uDEF4\uDEFE\uDEFF\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDCC7-\uDCCF\uDD00-\uDD43\uDD4B\uDD50-\uDD59]|\uD83B[\uDC71-\uDCAB\uDCAD-\uDCAF\uDCB1-\uDCB4\uDD01-\uDD2D\uDD2F-\uDD3D\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD83C[\uDD00-\uDD0C]|\uD83E[\uDFF0-\uDFF9]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEAD\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0\uDFF0-\uDFFF]|\uD87B[\uDC00-\uDE5D]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A\uDF50-\uDFFF]|\uD88D[\uDC00-\uDC79])+$/;
41
42
  var hasDestroy = function hasDestroy(client) {
42
43
  return 'destroy' in client && typeof client.destroy === 'function';
43
44
  };
@@ -45,9 +46,70 @@ var createInitialState = function createInitialState() {
45
46
  return {
46
47
  ghostText: '',
47
48
  ghostPosition: -1,
48
- decorationSet: DecorationSet.empty
49
+ decorationSet: DecorationSet.empty,
50
+ suggestion: null
49
51
  };
50
52
  };
53
+ var clearGhostState = function clearGhostState(pluginState) {
54
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
55
+ ghostText: '',
56
+ ghostPosition: -1,
57
+ decorationSet: DecorationSet.empty,
58
+ suggestion: null
59
+ });
60
+ };
61
+
62
+ /**
63
+ * Keep a displayed ghost alive when the user types the character it already
64
+ * predicted.
65
+ *
66
+ * Clearing on every document change throws away a suggestion the keystroke just
67
+ * confirmed, and the replacement decision has to re-earn it from an empty state
68
+ * inside the budget — which it often misses, so the ghost blinks out mid-word.
69
+ * It is worse for multi-word surfaces: typing the space in `machine learning`
70
+ * empties the current word, and the display gate refuses to show anything again
71
+ * until three characters of `learning` exist.
72
+ *
73
+ * A confirming keystroke is not new information about the prediction, so the
74
+ * ghost simply advances by one character and the still-running decision is free
75
+ * to replace it with something better. Anything else — a different character,
76
+ * a paste, a deletion, an edit elsewhere — clears as before.
77
+ */
78
+ var advanceGhostThroughTypedCharacter = function advanceGhostThroughTypedCharacter(tr, pluginState) {
79
+ var ghostPosition = pluginState.ghostPosition,
80
+ ghostText = pluginState.ghostText,
81
+ suggestion = pluginState.suggestion;
82
+ if (ghostText.length < 2 || ghostPosition < 0 || !suggestion) {
83
+ // A one-character tail leaves nothing to show after advancing.
84
+ return null;
85
+ }
86
+
87
+ // Exactly one character longer, with the cursor sitting just past the ghost
88
+ // anchor: together these identify a single insertion made at the anchor
89
+ // rather than a paste or an edit elsewhere in the document.
90
+ if (tr.doc.content.size - tr.before.content.size !== 1) {
91
+ return null;
92
+ }
93
+ if (!tr.selection.empty || tr.selection.from !== ghostPosition + 1) {
94
+ return null;
95
+ }
96
+ if (tr.doc.textBetween(ghostPosition, ghostPosition + 1) !== ghostText[0]) {
97
+ return null;
98
+ }
99
+ var remaining = ghostText.slice(1);
100
+ var nextPosition = ghostPosition + 1;
101
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
102
+ ghostText: remaining,
103
+ ghostPosition: nextPosition,
104
+ decorationSet: createGhostTextDecorationSet(tr.doc, nextPosition, remaining),
105
+ // `surface` stays the full candidate so cooldown and analytics still
106
+ // describe the suggestion that was originally committed.
107
+ suggestion: _objectSpread(_objectSpread({}, suggestion), {}, {
108
+ ghostText: remaining,
109
+ position: nextPosition
110
+ })
111
+ });
112
+ };
51
113
 
52
114
  /**
53
115
  * Extract text content before the cursor from the current document.
@@ -79,6 +141,10 @@ var getTextBeforeCursor = function getTextBeforeCursor(state) {
79
141
  }
80
142
  return fullText.slice(-maxChars);
81
143
  };
144
+ var getTrailingSurfacePrefixLength = function getTrailingSurfacePrefixLength(text) {
145
+ var _text$trimEnd$match$, _text$trimEnd$match;
146
+ return (_text$trimEnd$match$ = (_text$trimEnd$match = text.trimEnd().match(TRAILING_SURFACE_PREFIX_REGEX)) === null || _text$trimEnd$match === void 0 ? void 0 : _text$trimEnd$match[0].length) !== null && _text$trimEnd$match$ !== void 0 ? _text$trimEnd$match$ : 0;
147
+ };
82
148
 
83
149
  /**
84
150
  * Set the autocomplete state via a transaction metadata.
@@ -91,21 +157,39 @@ var setAutocompleteMeta = function setAutocompleteMeta(tr, meta) {
91
157
  * Apply a ghost text suggestion to the editor state.
92
158
  */
93
159
  var lastShownGhostText = '';
94
- var showGhostText = function showGhostText(view, text, position) {
160
+ var showGhostText = function showGhostText(view, prediction, position, revision, decisionStartedAt) {
95
161
  try {
96
162
  var state = view.state,
97
163
  dispatch = view.dispatch;
98
- var decorationSet = createGhostTextDecorationSet(state, position, text);
164
+ var suggestion = {
165
+ decisionLatencyMs: performance.now() - decisionStartedAt,
166
+ evidenceTier: prediction.evidenceTier,
167
+ ghostText: prediction.text,
168
+ meanTokenLogProbability: prediction.meanTokenLogProbability,
169
+ poolHeldExtension: prediction.poolHeldExtension,
170
+ position: position,
171
+ posterior: prediction.posterior,
172
+ rankScore: prediction.rankScore,
173
+ revision: revision,
174
+ shortlistSize: prediction.shortlistSize,
175
+ surface: prediction.surface,
176
+ termType: prediction.termType,
177
+ winnerMargin: prediction.winnerMargin
178
+ };
179
+ var decorationSet = createGhostTextDecorationSet(state.doc, position, prediction.text);
99
180
  var tr = setAutocompleteMeta(state.tr, {
100
- ghostText: text,
181
+ ghostText: prediction.text,
101
182
  ghostPosition: position,
102
- decorationSet: decorationSet
183
+ decorationSet: decorationSet,
184
+ suggestion: suggestion
103
185
  });
104
186
  dispatch(tr);
187
+ return suggestion;
105
188
  } catch (error) {
106
189
  logException(error, {
107
190
  location: 'editor-plugin-autocomplete/showGhostText'
108
191
  });
192
+ return null;
109
193
  }
110
194
  };
111
195
 
@@ -122,7 +206,8 @@ var clearGhostText = function clearGhostText(state, dispatch) {
122
206
  var tr = setAutocompleteMeta(state.tr, {
123
207
  ghostText: '',
124
208
  ghostPosition: -1,
125
- decorationSet: DecorationSet.empty
209
+ decorationSet: DecorationSet.empty,
210
+ suggestion: null
126
211
  });
127
212
  dispatch(tr);
128
213
  } catch (error) {
@@ -140,38 +225,41 @@ var clearGhostText = function clearGhostText(state, dispatch) {
140
225
  */
141
226
  var acceptGhostText = function acceptGhostText(state, dispatch) {
142
227
  var pluginState = autocompletePluginKey.getState(state);
143
- if (!pluginState || !pluginState.ghostText) {
144
- return false;
228
+ if (!(pluginState !== null && pluginState !== void 0 && pluginState.suggestion) || !pluginState.ghostText) {
229
+ return null;
145
230
  }
146
231
  if (dispatch) {
147
232
  try {
148
233
  var ghostText = pluginState.ghostText,
149
- ghostPosition = pluginState.ghostPosition;
234
+ ghostPosition = pluginState.ghostPosition,
235
+ suggestion = pluginState.suggestion;
150
236
  var tr = state.tr.insertText(ghostText, ghostPosition);
151
237
  tr = setAutocompleteMeta(tr, {
152
238
  ghostText: '',
153
239
  ghostPosition: -1,
154
- decorationSet: DecorationSet.empty
240
+ decorationSet: DecorationSet.empty,
241
+ suggestion: null
155
242
  });
156
243
  dispatch(tr);
244
+ return suggestion;
157
245
  } catch (error) {
158
246
  logException(error, {
159
247
  location: 'editor-plugin-autocomplete/acceptGhostText'
160
248
  });
161
- return false;
249
+ return null;
162
250
  }
163
251
  }
164
- return true;
252
+ return pluginState.suggestion;
165
253
  };
166
254
  var acceptGhostTextWithAnalytics = function acceptGhostTextWithAnalytics(state, dispatch, onAccepted) {
167
- var _pluginState$ghostTex;
168
- var pluginState = autocompletePluginKey.getState(state);
169
- var ghostText = (_pluginState$ghostTex = pluginState === null || pluginState === void 0 ? void 0 : pluginState.ghostText) !== null && _pluginState$ghostTex !== void 0 ? _pluginState$ghostTex : '';
170
- var accepted = acceptGhostText(state, dispatch);
171
- if (accepted) {
172
- onAccepted(ghostText);
255
+ var acceptedSuggestion = acceptGhostText(state, dispatch);
256
+ if (acceptedSuggestion) {
257
+ // Start the post-accept cooldown so the just-accepted unit isn't immediately
258
+ // re-offered (QI-2 echo, e.g. `end to end` → `end to end to end`).
259
+ noteSuggestionAccepted(acceptedSuggestion.surface);
260
+ onAccepted(acceptedSuggestion);
173
261
  }
174
- return accepted;
262
+ return acceptedSuggestion !== null;
175
263
  };
176
264
 
177
265
  /**
@@ -233,14 +321,6 @@ var getLeadingTextCharacter = function getLeadingTextCharacter(text) {
233
321
  }
234
322
  return String.fromCodePoint(firstCodePoint);
235
323
  };
236
- var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefore) {
237
- var _textBefore$match$, _textBefore$match;
238
- if (isWordBoundary(textBefore)) {
239
- return 0;
240
- }
241
- var trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(TRAILING_NON_BOUNDARY_TOKEN_REGEX)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
242
- return trailingToken.length;
243
- };
244
324
  var isEnglishLocale = function isEnglishLocale(locale) {
245
325
  if (!locale) {
246
326
  return false;
@@ -248,11 +328,12 @@ var isEnglishLocale = function isEnglishLocale(locale) {
248
328
  return locale.toLowerCase().startsWith('en');
249
329
  };
250
330
  export var createAutocompletePlugin = function createAutocompletePlugin(options, api) {
251
- var _options$surface, _options$locale;
331
+ var _options$surface, _options$locale, _registeredSlowLaneSt;
252
332
  var surface = (_options$surface = options === null || options === void 0 ? void 0 : options.surface) !== null && _options$surface !== void 0 ? _options$surface : 'editor';
253
333
  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;
254
334
  var isAutocompleteEnabled = isEnglishLocale(locale);
255
335
  var debounceTimer = null;
336
+ var decisionDeadlineTimer = null;
256
337
  var hasIngestedPage = false;
257
338
  var resolvedContext;
258
339
  /**
@@ -262,6 +343,10 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
262
343
  */
263
344
  var currentView = null;
264
345
  var unsubscribeFromContextUpdates;
346
+ // "Prime, don't ask-and-read": semantic, Tier-A, and Tier-B results land
347
+ // asynchronously, often with no following keystroke. Re-run prediction when
348
+ // they arrive so the caches are consumed immediately.
349
+ var reschedulePredictionOnSlowLaneResult = null;
265
350
  /**
266
351
  * Set after accepting a suggestion so the next doc-change update
267
352
  * skips scheduling a new prediction for the just-inserted text.
@@ -275,8 +360,8 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
275
360
  * re-showing the same suggestion. Resets to null as soon as the text changes.
276
361
  */
277
362
  var dismissedContext = null;
278
- var lastSuggestionTypedLength = 0;
279
- var lastSuggestionLength = 0;
363
+ var decisionRevision = 0;
364
+ var activeDecision;
280
365
 
281
366
  /**
282
367
  * cold → no slow-lane vector received yet; frequency-only trie scoring
@@ -341,10 +426,10 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
341
426
  }
342
427
  });
343
428
  };
344
- var fireSuggestionInsertedAnalytics = function fireSuggestionInsertedAnalytics(ghostText) {
429
+ var fireSuggestionInsertedAnalytics = function fireSuggestionInsertedAnalytics(suggestion) {
345
430
  var _api$analytics4;
346
- var typedLength = lastSuggestionTypedLength;
347
- var suggestionLength = lastSuggestionLength || typedLength + ghostText.length;
431
+ var typedLength = Math.max(0, suggestion.surface.length - suggestion.ghostText.length);
432
+ var suggestionLength = suggestion.surface.length;
348
433
  var kssDelta = suggestionLength - typedLength;
349
434
  api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 || _api$analytics4.actions.fireAnalyticsEvent({
350
435
  action: ACTION.SUGGESTION_INSERTED,
@@ -363,13 +448,59 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
363
448
  debounceMs: LOCAL_SLOW_LANE_DEBOUNCE_MS,
364
449
  onLoadSuccess: fireLocalModelLoadedAnalytics,
365
450
  onLoadError: fireLocalModelLoadFailedAnalytics,
451
+ // Re-run prediction for each independently scheduled model result.
452
+ onUpdate: function onUpdate() {
453
+ var _reschedulePrediction;
454
+ return (_reschedulePrediction = reschedulePredictionOnSlowLaneResult) === null || _reschedulePrediction === void 0 ? void 0 : _reschedulePrediction({
455
+ kind: 'semantic'
456
+ });
457
+ },
458
+ onBoundaryLmUpdate: function onBoundaryLmUpdate(_ref) {
459
+ var _reschedulePrediction2;
460
+ var contextKey = _ref.contextKey,
461
+ familyKey = _ref.familyKey,
462
+ latencyMs = _ref.latencyMs;
463
+ return (_reschedulePrediction2 = reschedulePredictionOnSlowLaneResult) === null || _reschedulePrediction2 === void 0 ? void 0 : _reschedulePrediction2({
464
+ contextKey: contextKey,
465
+ familyKey: familyKey,
466
+ kind: 'boundary',
467
+ latencyMs: latencyMs
468
+ });
469
+ },
470
+ onSurfaceScoreUpdate: function onSurfaceScoreUpdate(_ref2) {
471
+ var _reschedulePrediction3;
472
+ var contextKey = _ref2.contextKey,
473
+ familyKey = _ref2.familyKey,
474
+ latencyMs = _ref2.latencyMs;
475
+ return (_reschedulePrediction3 = reschedulePredictionOnSlowLaneResult) === null || _reschedulePrediction3 === void 0 ? void 0 : _reschedulePrediction3({
476
+ contextKey: contextKey,
477
+ familyKey: familyKey,
478
+ kind: 'surface',
479
+ latencyMs: latencyMs
480
+ });
481
+ },
366
482
  surface: surface
367
483
  }) : createSlowLaneClient({
368
484
  baseUrl: '',
369
485
  debounceMs: NETWORK_SLOW_LANE_DEBOUNCE_MS,
486
+ // Re-run prediction when the BE semantic vector / LM logits land so
487
+ // warm-mode unigram ranking applies without waiting for a keystroke.
488
+ onUpdate: function onUpdate() {
489
+ var _reschedulePrediction4;
490
+ return (_reschedulePrediction4 = reschedulePredictionOnSlowLaneResult) === null || _reschedulePrediction4 === void 0 ? void 0 : _reschedulePrediction4({
491
+ kind: 'semantic'
492
+ });
493
+ },
370
494
  surface: surface
371
495
  });
372
- setDefaultSlowLaneClient(slowLaneClient);
496
+ var slowLaneClientKind = options !== null && options !== void 0 && options.useLocalModel ? 'localLlm' : 'server';
497
+ var slowLaneClientId = "".concat(surface, ":").concat(slowLaneClientKind, ":").concat(++slowLaneClientInstanceSequence);
498
+ setDefaultSlowLaneClient(slowLaneClient, {
499
+ clientId: slowLaneClientId,
500
+ kind: slowLaneClientKind
501
+ });
502
+ var registeredSlowLaneStatus = getDefaultSlowLaneClientStatus();
503
+ ctcTag('init', "slow lane registered \xB7 id=".concat(slowLaneClientId, " \xB7 selected=").concat(slowLaneClientKind, " \xB7 canonical=").concat(registeredSlowLaneStatus.canonicalScoringSupported ? 'yes' : 'no', " \xB7 ready=").concat((_registeredSlowLaneSt = registeredSlowLaneStatus.localModelReady) !== null && _registeredSlowLaneSt !== void 0 ? _registeredSlowLaneSt : 'n/a'), slowLaneClientKind === 'localLlm' ? CTC_STYLES.good : CTC_STYLES.warn);
373
504
  var contextRequestInFlight = false;
374
505
  var lastContextRefreshAt = 0;
375
506
  // Bounds the word-boundary retry loop so it terminates even when the editor is
@@ -378,7 +509,72 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
378
509
  // Set when the plugin is torn down so in-flight getContext() resolutions don't
379
510
  // mutate the global text-predictor state after destruction.
380
511
  var destroyed = false;
381
- var ingestedContextTexts = new Set();
512
+ // L1 ingestion cannot run before the vocabulary has loaded: `incrementSessionFreq`
513
+ // only touches trie nodes that already exist, so an empty trie silently rejects
514
+ // every word. Text that arrives first — which a page usually does, since the
515
+ // chat resolves it on focus in the same tick as the load starts — is held here
516
+ // and applied once the load has settled.
517
+ var pendingSessionContextTexts = new Set();
518
+ var sessionIngestedContextTexts = new Set();
519
+ var isVocabularyReady = false;
520
+ var vocabularyLoadPromise;
521
+ var addBoundedContextText = function addBoundedContextText(texts, text) {
522
+ texts.add(text);
523
+ // Evict oldest entries (Set preserves insertion order) to bound memory.
524
+ while (texts.size > MAX_INGESTED_CONTEXT_TEXTS) {
525
+ var oldest = texts.values().next().value;
526
+ if (oldest === undefined) {
527
+ break;
528
+ }
529
+ texts.delete(oldest);
530
+ }
531
+ };
532
+ var flushPendingSessionContext = function flushPendingSessionContext() {
533
+ if (!isVocabularyReady || destroyed) {
534
+ return;
535
+ }
536
+ var _iterator = _createForOfIteratorHelper(pendingSessionContextTexts),
537
+ _step;
538
+ try {
539
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
540
+ var text = _step.value;
541
+ if (!sessionIngestedContextTexts.has(text)) {
542
+ ingestDocumentPage(text);
543
+ addBoundedContextText(sessionIngestedContextTexts, text);
544
+ }
545
+ pendingSessionContextTexts.delete(text);
546
+ }
547
+ } catch (err) {
548
+ _iterator.e(err);
549
+ } finally {
550
+ _iterator.f();
551
+ }
552
+ };
553
+ var ensureVocabularyReady = function ensureVocabularyReady() {
554
+ var _options$useLocalMode;
555
+ if (isVocabularyReady) {
556
+ flushPendingSessionContext();
557
+ return Promise.resolve();
558
+ }
559
+ vocabularyLoadPromise !== null && vocabularyLoadPromise !== void 0 ? vocabularyLoadPromise : vocabularyLoadPromise = loadDefaultVocabulary({
560
+ isLocalLLM: (_options$useLocalMode = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode !== void 0 ? _options$useLocalMode : false,
561
+ surface: surface
562
+ }).then(function () {
563
+ if (destroyed) {
564
+ return;
565
+ }
566
+ isVocabularyReady = true;
567
+ flushPendingSessionContext();
568
+ }).catch(function (error) {
569
+ // Do not consume the pending text on failure. A later focus retries the
570
+ // load and can still apply the original context exactly once.
571
+ vocabularyLoadPromise = undefined;
572
+ logException(error, {
573
+ location: 'editor-plugin-autocomplete/loadDefaultVocabulary'
574
+ });
575
+ });
576
+ return vocabularyLoadPromise;
577
+ };
382
578
  var logContextResolved = function logContextResolved(source, context) {
383
579
  var _context$parentCommen, _context$siblingComme4, _context$siblingComme5;
384
580
  if (!isAutocompleteDebugEnabled()) {
@@ -386,7 +582,7 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
386
582
  }
387
583
 
388
584
  // eslint-disable-next-line no-console
389
- console.log('%c[Autocomplete] %cgetContext resolved', 'color: #00b8d9; font-weight: bold;', 'color: inherit;', {
585
+ console.log('%c[CTC:signal]%c getContext resolved', CTC_STYLES.brand, CTC_STYLES.body, {
390
586
  source: source,
391
587
  hasParentComment: !!(context !== null && context !== void 0 && context.parentCommentContent),
392
588
  parentCommentPreview: context === null || context === void 0 || (_context$parentCommen = context.parentCommentContent) === null || _context$parentCommen === void 0 ? void 0 : _context$parentCommen.slice(0, 80),
@@ -401,44 +597,36 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
401
597
  // fullPageContent, so replacing would drop previously resolved context.
402
598
  // Strip undefined values first so a field explicitly set to undefined by
403
599
  // getContext doesn't overwrite a previously resolved value.
404
- var definedContext = Object.fromEntries(Object.entries(context).filter(function (_ref) {
405
- var _ref2 = _slicedToArray(_ref, 2),
406
- value = _ref2[1];
600
+ var definedContext = Object.fromEntries(Object.entries(context).filter(function (_ref3) {
601
+ var _ref4 = _slicedToArray(_ref3, 2),
602
+ value = _ref4[1];
407
603
  return value !== undefined;
408
604
  }));
409
605
  resolvedContext = _objectSpread(_objectSpread({}, resolvedContext), definedContext);
410
606
  var ingestContextText = function ingestContextText(text) {
411
- if (!text || ingestedContextTexts.has(text)) {
607
+ if (!text || sessionIngestedContextTexts.has(text)) {
412
608
  return;
413
609
  }
414
- ingestedContextTexts.add(text);
415
- // Evict oldest entries (Set preserves insertion order) to bound memory.
416
- while (ingestedContextTexts.size > MAX_INGESTED_CONTEXT_TEXTS) {
417
- var oldest = ingestedContextTexts.values().next().value;
418
- if (oldest === undefined) {
419
- break;
420
- }
421
- ingestedContextTexts.delete(oldest);
422
- }
423
- ingestDocumentPage(text);
610
+ addBoundedContextText(pendingSessionContextTexts, text);
424
611
  };
425
612
  ingestContextText(context.fullPageContent);
426
613
  ingestContextText(context.parentCommentContent);
427
- var _iterator = _createForOfIteratorHelper((_context$siblingComme6 = context.siblingCommentsContents) !== null && _context$siblingComme6 !== void 0 ? _context$siblingComme6 : []),
428
- _step;
614
+ var _iterator2 = _createForOfIteratorHelper((_context$siblingComme6 = context.siblingCommentsContents) !== null && _context$siblingComme6 !== void 0 ? _context$siblingComme6 : []),
615
+ _step2;
429
616
  try {
430
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
431
- var siblingCommentContent = _step.value;
617
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
618
+ var siblingCommentContent = _step2.value;
432
619
  ingestContextText(siblingCommentContent);
433
620
  }
434
-
435
- // Context arrived after word boundaries may already have fired. Re-send
436
- // slow-lane context immediately so the next inference includes the thread.
437
621
  } catch (err) {
438
- _iterator.e(err);
622
+ _iterator2.e(err);
439
623
  } finally {
440
- _iterator.f();
624
+ _iterator2.f();
441
625
  }
626
+ flushPendingSessionContext();
627
+
628
+ // Context arrived after word boundaries may already have fired. Re-send
629
+ // slow-lane context immediately so the next inference includes the thread.
442
630
  if (currentView) {
443
631
  slowLaneClient.updateContext(buildSlowLaneText(currentView.state.doc.textContent, resolvedContext));
444
632
  }
@@ -449,10 +637,10 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
449
637
  * (no getContext, a request already in flight, or throttled). Callers that
450
638
  * track a retry budget should only count attempts where this returns true.
451
639
  */
452
- var refreshContext = function refreshContext(_ref3) {
453
- var source = _ref3.source,
454
- _ref3$allowThrottle = _ref3.allowThrottle,
455
- allowThrottle = _ref3$allowThrottle === void 0 ? true : _ref3$allowThrottle;
640
+ var refreshContext = function refreshContext(_ref5) {
641
+ var source = _ref5.source,
642
+ _ref5$allowThrottle = _ref5.allowThrottle,
643
+ allowThrottle = _ref5$allowThrottle === void 0 ? true : _ref5$allowThrottle;
456
644
  if (!(options !== null && options !== void 0 && options.getContext) || contextRequestInFlight) {
457
645
  return false;
458
646
  }
@@ -483,73 +671,230 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
483
671
  });
484
672
  return true;
485
673
  };
486
-
487
- /**
488
- * Schedule a prediction after a short debounce.
489
- * Tier 1 predictions are synchronous (<0.1ms) but we still debounce
490
- * to avoid unnecessary work on rapid keystrokes.
491
- */
492
- var schedulePrediction = function schedulePrediction(view) {
674
+ var clearDecisionTimers = function clearDecisionTimers() {
493
675
  if (debounceTimer) {
494
676
  clearTimeout(debounceTimer);
677
+ debounceTimer = null;
495
678
  }
496
- debounceTimer = setTimeout(function () {
497
- try {
498
- var state = view.state;
499
- var selection = state.selection;
500
- if (!selection.empty) {
501
- return;
502
- }
679
+ if (decisionDeadlineTimer) {
680
+ clearTimeout(decisionDeadlineTimer);
681
+ decisionDeadlineTimer = null;
682
+ }
683
+ };
684
+ var cancelActiveDecision = function cancelActiveDecision() {
685
+ clearDecisionTimers();
686
+ activeDecision = undefined;
687
+ };
688
+ var isDecisionSnapshotCurrent = function isDecisionSnapshotCurrent(view, decision) {
689
+ var state = view.state;
690
+ return state.selection.empty && state.selection.from === decision.position && getTextBeforeCursor(state) === decision.textBefore;
691
+ };
692
+ var isDecisionCurrent = function isDecisionCurrent(view, decision) {
693
+ var _activeDecision;
694
+ return ((_activeDecision = activeDecision) === null || _activeDecision === void 0 ? void 0 : _activeDecision.revision) === decision.revision && isDecisionSnapshotCurrent(view, decision);
695
+ };
696
+ var expireActiveDecision = function expireActiveDecision(decision) {
697
+ var _activeDecision2;
698
+ if (((_activeDecision2 = activeDecision) === null || _activeDecision2 === void 0 ? void 0 : _activeDecision2.revision) !== decision.revision) {
699
+ return;
700
+ }
701
+ cancelActiveDecision();
702
+ };
703
+ var evaluateActiveDecision = function evaluateActiveDecision(view, revision) {
704
+ var decision = activeDecision;
705
+ if (!decision || decision.revision !== revision || !isDecisionCurrent(view, decision)) {
706
+ return;
707
+ }
708
+ if (performance.now() > decision.deadlineAt) {
709
+ expireActiveDecision(decision);
710
+ return;
711
+ }
712
+ decision.evaluations++;
713
+ try {
714
+ var state = view.state;
715
+ var selection = state.selection;
503
716
 
504
- // Suppress suggestions when the cursor is mid-word — only offer
505
- // completions when the cursor is at the trailing edge of a token.
506
- // nodeAfter correctly handles inline atoms (mentions, emojis) where
507
- // parentOffset and textContent indices diverge.
508
- var nodeAfter = selection.$from.nodeAfter;
509
- var charAfterCursor = nodeAfter !== null && nodeAfter !== void 0 && nodeAfter.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
510
- // Only suppress for Unicode letters, digits, and underscore — hyphens,
511
- // apostrophes, and similar punctuation are valid left-edge boundaries
512
- // and should not block suggestions (e.g. cursor before '-' in compound-word).
513
- if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
514
- return;
515
- }
516
- var textBefore = getTextBeforeCursor(state);
717
+ // Suppress suggestions when the cursor is mid-word — only offer
718
+ // completions when the cursor is at the trailing edge of a token.
719
+ var nodeAfter = selection.$from.nodeAfter;
720
+ var charAfterCursor = nodeAfter !== null && nodeAfter !== void 0 && nodeAfter.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
721
+ if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
722
+ cancelActiveDecision();
723
+ return;
724
+ }
725
+ if (decision.textBefore === dismissedContext) {
726
+ cancelActiveDecision();
727
+ return;
728
+ }
729
+ dismissedContext = null;
517
730
 
518
- // Suppress re-showing the same suggestion the user just dismissed.
519
- // Once the text context changes (user types or deletes), this clears automatically.
520
- if (textBefore === dismissedContext) {
521
- return;
522
- }
523
- dismissedContext = null;
524
- if (textBefore.trim().length < 3) {
525
- return;
731
+ // Two characters are enough to prefetch the canonical shortlist. The
732
+ // predictor itself withholds display until the three-character gate.
733
+ if (decision.textBefore.trim().length < 2) {
734
+ cancelActiveDecision();
735
+ return;
736
+ }
737
+ var prediction = predict(decision.textBefore);
738
+ if (!prediction || prediction.text.length === 0) {
739
+ // Remain in `collecting` until an async signal arrives or the hard
740
+ // deadline expires. We never display a provisional fallback.
741
+ return;
742
+ }
743
+ var readyAtMs = performance.now() - decision.startedAt;
744
+ if (readyAtMs > GHOST_DECISION_BUDGET_MS) {
745
+ cancelActiveDecision();
746
+ if (isAutocompleteDebugEnabled()) {
747
+ // eslint-disable-next-line no-console
748
+ console.log('%c[CTC:budget]%c fully gated after render deadline (render suppressed)', CTC_STYLES.section, CTC_STYLES.body, {
749
+ revision: decision.revision,
750
+ readyAtMs: readyAtMs,
751
+ surface: prediction.surface
752
+ });
526
753
  }
527
- var prediction = predict(textBefore);
528
- if (prediction && prediction.length > 0) {
529
- var typedLength = getTypedLengthForPrediction(textBefore);
530
- lastSuggestionTypedLength = typedLength;
531
- lastSuggestionLength = typedLength + prediction.length;
532
- showGhostText(view, prediction, selection.from);
533
- if (prediction !== lastShownGhostText) {
534
- var _api$analytics5;
535
- lastShownGhostText = prediction;
536
- api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 || _api$analytics5.actions.fireAnalyticsEvent({
537
- action: ACTION.SUGGESTION_VIEWED,
538
- actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
539
- eventType: EVENT_TYPE.TRACK,
540
- attributes: {
541
- completionSource: getCompletionSource(),
542
- surface: surface
543
- }
544
- });
754
+ return;
755
+ }
756
+ var suggestion = showGhostText(view, prediction, decision.position, decision.revision, decision.startedAt);
757
+ if (!suggestion) {
758
+ cancelActiveDecision();
759
+ return;
760
+ }
761
+ cancelActiveDecision();
762
+ if (isAutocompleteDebugEnabled()) {
763
+ // eslint-disable-next-line no-console
764
+ console.log('%c[CTC:decision]%c committed immutable ghost', CTC_STYLES.brand, CTC_STYLES.body, {
765
+ revision: suggestion.revision,
766
+ surface: suggestion.surface,
767
+ ghostText: suggestion.ghostText,
768
+ evidenceTier: suggestion.evidenceTier,
769
+ rankScore: suggestion.rankScore,
770
+ winnerMargin: suggestion.winnerMargin,
771
+ decisionLatencyMs: suggestion.decisionLatencyMs,
772
+ budgetMs: GHOST_DECISION_BUDGET_MS,
773
+ typedPrefixLength: getTrailingSurfacePrefixLength(decision.textBefore),
774
+ evaluations: decision.evaluations,
775
+ asyncSignals: decision.asyncSignals,
776
+ readyWithinBudget: suggestion.decisionLatencyMs <= GHOST_DECISION_BUDGET_MS
777
+ });
778
+ }
779
+ if (suggestion.ghostText !== lastShownGhostText) {
780
+ var _api$analytics5;
781
+ lastShownGhostText = suggestion.ghostText;
782
+ api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 || _api$analytics5.actions.fireAnalyticsEvent({
783
+ action: ACTION.SUGGESTION_VIEWED,
784
+ actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
785
+ eventType: EVENT_TYPE.TRACK,
786
+ attributes: {
787
+ completionSource: getCompletionSource(),
788
+ surface: surface
545
789
  }
546
- }
547
- } catch (error) {
548
- logException(error, {
549
- location: 'editor-plugin-autocomplete/schedulePrediction'
550
790
  });
551
791
  }
552
- }, DEBOUNCE_MS);
792
+ } catch (error) {
793
+ cancelActiveDecision();
794
+ logException(error, {
795
+ location: 'editor-plugin-autocomplete/evaluateActiveDecision'
796
+ });
797
+ }
798
+ };
799
+ var beginPredictionDecision = function beginPredictionDecision(view) {
800
+ cancelActiveDecision();
801
+ var state = view.state;
802
+ if (!state.selection.empty) {
803
+ return;
804
+ }
805
+ var startedAt = performance.now();
806
+ var textBefore = getTextBeforeCursor(state);
807
+ var revision = ++decisionRevision;
808
+ activeDecision = {
809
+ asyncSignals: 0,
810
+ deadlineAt: startedAt + GHOST_DECISION_BUDGET_MS,
811
+ evaluations: 0,
812
+ position: state.selection.from,
813
+ revision: revision,
814
+ startedAt: startedAt,
815
+ textBefore: textBefore
816
+ };
817
+ if (isAutocompleteDebugEnabled()) {
818
+ // eslint-disable-next-line no-console
819
+ console.log('%c[CTC:decision]%c collecting', CTC_STYLES.brand, CTC_STYLES.body, {
820
+ revision: revision,
821
+ budgetMs: GHOST_DECISION_BUDGET_MS,
822
+ coalesceMs: PREDICTION_COALESCE_MS,
823
+ typedPrefixLength: getTrailingSurfacePrefixLength(activeDecision.textBefore),
824
+ textBefore: activeDecision.textBefore.slice(-80)
825
+ });
826
+ }
827
+ debounceTimer = setTimeout(function () {
828
+ debounceTimer = null;
829
+ evaluateActiveDecision(view, revision);
830
+ }, PREDICTION_COALESCE_MS);
831
+ decisionDeadlineTimer = setTimeout(function () {
832
+ var _activeDecision3;
833
+ decisionDeadlineTimer = null;
834
+ if (((_activeDecision3 = activeDecision) === null || _activeDecision3 === void 0 ? void 0 : _activeDecision3.revision) === revision) {
835
+ var expiredDecision = activeDecision;
836
+ if (isAutocompleteDebugEnabled()) {
837
+ var elapsedMs = performance.now() - expiredDecision.startedAt;
838
+ var predictionDebug = getLastPredictionDebug();
839
+ var matchingPredictionDebug = (predictionDebug === null || predictionDebug === void 0 ? void 0 : predictionDebug.textBefore) === expiredDecision.textBefore ? predictionDebug : null;
840
+ var abstentionPrefix = '— abstain: ';
841
+ var predictorDecision = matchingPredictionDebug === null || matchingPredictionDebug === void 0 ? void 0 : matchingPredictionDebug.decision;
842
+ var predictorReason = predictorDecision !== null && predictorDecision !== void 0 && predictorDecision.startsWith(abstentionPrefix) ? predictorDecision.slice(abstentionPrefix.length) : predictorDecision;
843
+ var decisionMessage = predictorReason && !(matchingPredictionDebug !== null && matchingPredictionDebug !== void 0 && matchingPredictionDebug.awaitingAsyncEvidence) ? "abstain: ".concat(predictorReason) : "abstain: ".concat(GHOST_DECISION_BUDGET_MS, "ms decision budget expired").concat(predictorReason ? " \xB7 ".concat(predictorReason) : '');
844
+ // eslint-disable-next-line no-console
845
+ console.log("%c[CTC:decision]%c ".concat(decisionMessage), CTC_STYLES.brand, CTC_STYLES.body, {
846
+ revision: revision,
847
+ budgetMs: GHOST_DECISION_BUDGET_MS,
848
+ elapsedMs: elapsedMs,
849
+ typedPrefixLength: getTrailingSurfacePrefixLength(expiredDecision.textBefore),
850
+ evaluations: expiredDecision.evaluations,
851
+ asyncSignals: expiredDecision.asyncSignals,
852
+ textBefore: expiredDecision.textBefore.slice(-80),
853
+ predictorDecision: predictorReason !== null && predictorReason !== void 0 ? predictorReason : null
854
+ });
855
+ }
856
+ expireActiveDecision(expiredDecision);
857
+ }
858
+ }, GHOST_DECISION_BUDGET_MS);
859
+ };
860
+
861
+ // Async semantic/canonical results may complete the active decision, but they
862
+ // never create a new decision or replace an already committed ghost.
863
+ reschedulePredictionOnSlowLaneResult = function reschedulePredictionOnSlowLaneResult(signal) {
864
+ if (destroyed || !currentView) {
865
+ return;
866
+ }
867
+ if (!activeDecision) {
868
+ return;
869
+ }
870
+ var revision = activeDecision.revision;
871
+ var elapsedMs = performance.now() - activeDecision.startedAt;
872
+ var remainingMs = activeDecision.deadlineAt - performance.now();
873
+ if (remainingMs < 0) {
874
+ var expiredDecision = activeDecision;
875
+ expireActiveDecision(expiredDecision);
876
+ return;
877
+ }
878
+ activeDecision.asyncSignals++;
879
+ if (isAutocompleteDebugVerbose()) {
880
+ // eslint-disable-next-line no-console
881
+ console.log('%c[CTC:readiness]%c async evidence signal', CTC_STYLES.section, CTC_STYLES.body, _objectSpread(_objectSpread({}, signal), {}, {
882
+ revision: revision,
883
+ elapsedMs: elapsedMs,
884
+ remainingMs: Math.max(0, remainingMs),
885
+ typedPrefixLength: getTrailingSurfacePrefixLength(activeDecision.textBefore),
886
+ withinBudget: remainingMs >= 0
887
+ }));
888
+ }
889
+ if (debounceTimer) {
890
+ clearTimeout(debounceTimer);
891
+ }
892
+ debounceTimer = setTimeout(function () {
893
+ debounceTimer = null;
894
+ if (currentView) {
895
+ evaluateActiveDecision(currentView, revision);
896
+ }
897
+ }, 0);
553
898
  };
554
899
  var maybeUpdateSessionFrequency = function maybeUpdateSessionFrequency(view, prevState) {
555
900
  var newText = getTextBeforeCursor(view.state);
@@ -587,20 +932,14 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
587
932
  return _objectSpread(_objectSpread({}, pluginState), meta);
588
933
  }
589
934
 
590
- // A new prediction is scheduled from view.update.
935
+ // A new prediction is scheduled from view.update either way; the
936
+ // surviving ghost only covers the gap until it commits.
591
937
  if (tr.docChanged) {
592
- return _objectSpread(_objectSpread({}, pluginState), {}, {
593
- ghostText: '',
594
- ghostPosition: -1,
595
- decorationSet: DecorationSet.empty
596
- });
938
+ var _advanceGhostThroughT;
939
+ return (_advanceGhostThroughT = advanceGhostThroughTypedCharacter(tr, pluginState)) !== null && _advanceGhostThroughT !== void 0 ? _advanceGhostThroughT : clearGhostState(pluginState);
597
940
  }
598
941
  if (tr.selectionSet && pluginState.ghostText) {
599
- return _objectSpread(_objectSpread({}, pluginState), {}, {
600
- ghostText: '',
601
- ghostPosition: -1,
602
- decorationSet: DecorationSet.empty
603
- });
942
+ return clearGhostState(pluginState);
604
943
  }
605
944
  return pluginState;
606
945
  }
@@ -613,22 +952,25 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
613
952
  },
614
953
  handleKeyDown: keydownHandler({
615
954
  Tab: function Tab(state, dispatch) {
616
- return acceptGhostTextWithAnalytics(state, dispatch, function (ghostText) {
955
+ return acceptGhostTextWithAnalytics(state, dispatch, function (suggestion) {
617
956
  justAccepted = true;
618
957
  lastShownGhostText = '';
619
- fireSuggestionInsertedAnalytics(ghostText);
958
+ cancelActiveDecision();
959
+ fireSuggestionInsertedAnalytics(suggestion);
620
960
  });
621
961
  },
622
962
  ArrowRight: function ArrowRight(state, dispatch) {
623
- return acceptGhostTextWithAnalytics(state, dispatch, function (ghostText) {
963
+ return acceptGhostTextWithAnalytics(state, dispatch, function (suggestion) {
624
964
  justAccepted = true;
625
965
  lastShownGhostText = '';
626
- fireSuggestionInsertedAnalytics(ghostText);
966
+ cancelActiveDecision();
967
+ fireSuggestionInsertedAnalytics(suggestion);
627
968
  });
628
969
  },
629
970
  Escape: function Escape(state, dispatch) {
630
971
  var didClear = clearGhostText(state, dispatch);
631
972
  if (didClear) {
973
+ cancelActiveDecision();
632
974
  dismissedContext = getTextBeforeCursor(state);
633
975
  fireSuggestionDismissedAnalytics('escape');
634
976
  }
@@ -643,6 +985,7 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
643
985
  var pluginState = autocompletePluginKey.getState(view.state);
644
986
  if (pluginState !== null && pluginState !== void 0 && pluginState.ghostText) {
645
987
  clearGhostText(view.state, view.dispatch);
988
+ cancelActiveDecision();
646
989
  fireSuggestionDismissedAnalytics('blur');
647
990
  }
648
991
  return false;
@@ -654,6 +997,7 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
654
997
  if (!event.target.closest('[data-autocomplete-ghost="true"]')) {
655
998
  var didClear = clearGhostText(view.state, view.dispatch);
656
999
  if (didClear) {
1000
+ cancelActiveDecision();
657
1001
  dismissedContext = getTextBeforeCursor(view.state);
658
1002
  lastShownGhostText = '';
659
1003
  fireSuggestionDismissedAnalytics('click');
@@ -661,10 +1005,11 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
661
1005
  return false;
662
1006
  }
663
1007
  event.preventDefault();
664
- var accepted = acceptGhostTextWithAnalytics(view.state, view.dispatch, function (ghostText) {
1008
+ var accepted = acceptGhostTextWithAnalytics(view.state, view.dispatch, function (suggestion) {
665
1009
  justAccepted = true;
666
1010
  lastShownGhostText = '';
667
- fireSuggestionInsertedAnalytics(ghostText);
1011
+ cancelActiveDecision();
1012
+ fireSuggestionInsertedAnalytics(suggestion);
668
1013
  });
669
1014
  if (accepted) {
670
1015
  view.focus();
@@ -672,18 +1017,11 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
672
1017
  return accepted;
673
1018
  },
674
1019
  focus: function focus() {
675
- var _options$useLocalMode, _options$useLocalMode2, _options$useLocalMode3;
1020
+ var _options$useLocalMode2, _options$useLocalMode3;
676
1021
  if (!isAutocompleteEnabled) {
677
1022
  return false;
678
1023
  }
679
- loadDefaultVocabulary({
680
- isLocalLLM: (_options$useLocalMode = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode !== void 0 ? _options$useLocalMode : false,
681
- surface: surface
682
- }).catch(function (error) {
683
- logException(error, {
684
- location: 'editor-plugin-autocomplete/loadDefaultVocabulary'
685
- });
686
- });
1024
+ void ensureVocabularyReady();
687
1025
  loadVectorsAsync({
688
1026
  isLocalLLM: (_options$useLocalMode2 = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode2 !== void 0 ? _options$useLocalMode2 : false,
689
1027
  surface: surface
@@ -739,9 +1077,7 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
739
1077
  // Snapshot the post-acceptance text so follow-up transactions hit
740
1078
  // the dismissedContext guard and abort until the user types again.
741
1079
  dismissedContext = getTextBeforeCursor(view.state);
742
- if (debounceTimer) {
743
- clearTimeout(debounceTimer);
744
- }
1080
+ cancelActiveDecision();
745
1081
  return;
746
1082
  }
747
1083
  maybeUpdateSessionFrequency(view, prevState);
@@ -766,7 +1102,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
766
1102
  }
767
1103
  }
768
1104
  }
769
- schedulePrediction(view);
1105
+ beginPredictionDecision(view);
1106
+ } else if (!prevState.selection.eq(view.state.selection)) {
1107
+ cancelActiveDecision();
770
1108
  }
771
1109
  },
772
1110
  destroy: function destroy() {
@@ -775,14 +1113,14 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
775
1113
  currentView = null;
776
1114
  (_unsubscribeFromConte = unsubscribeFromContextUpdates) === null || _unsubscribeFromConte === void 0 || _unsubscribeFromConte();
777
1115
  unsubscribeFromContextUpdates = undefined;
778
- if (debounceTimer) {
779
- clearTimeout(debounceTimer);
780
- }
1116
+ cancelActiveDecision();
1117
+ var releasedDefaultClient = clearDefaultSlowLaneClient(slowLaneClient);
1118
+ ctcTag('init', "slow lane teardown \xB7 id=".concat(slowLaneClientId, " \xB7 default=").concat(releasedDefaultClient ? 'released' : 'retained newer owner'), releasedDefaultClient ? CTC_STYLES.dim : CTC_STYLES.warn);
781
1119
  if (hasDestroy(slowLaneClient)) {
782
1120
  slowLaneClient.destroy();
783
1121
  }
784
- ingestedContextTexts.clear();
785
- setDefaultSlowLaneClient(null);
1122
+ pendingSessionContextTexts.clear();
1123
+ sessionIngestedContextTexts.clear();
786
1124
  }
787
1125
  };
788
1126
  }