@atlaskit/editor-plugin-autocomplete 7.0.0 → 9.0.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 (80) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/compass.yml +2 -2
  3. package/dist/cjs/analytics/ufo.js +2 -1
  4. package/dist/cjs/pm-plugins/artifact-loader.js +135 -0
  5. package/dist/cjs/pm-plugins/artifacts-manifest.js +3 -0
  6. package/dist/cjs/pm-plugins/autocomplete-plugin.js +416 -129
  7. package/dist/cjs/pm-plugins/canonical-lm-scoring.js +371 -0
  8. package/dist/cjs/pm-plugins/debug-mode.js +90 -7
  9. package/dist/cjs/pm-plugins/ghost-text-decoration.js +12 -3
  10. package/dist/cjs/pm-plugins/local-slow-lane-client.js +1202 -453
  11. package/dist/cjs/pm-plugins/scoring-pipeline.js +164 -19
  12. package/dist/cjs/pm-plugins/slow-lane-client.js +100 -37
  13. package/dist/cjs/pm-plugins/text-predictor.js +1951 -273
  14. package/dist/es2019/analytics/ufo.js +2 -0
  15. package/dist/es2019/pm-plugins/artifact-loader.js +70 -0
  16. package/dist/es2019/pm-plugins/artifacts-manifest.js +3 -0
  17. package/dist/es2019/pm-plugins/autocomplete-plugin.js +425 -130
  18. package/dist/es2019/pm-plugins/canonical-lm-scoring.js +270 -0
  19. package/dist/es2019/pm-plugins/debug-mode.js +87 -7
  20. package/dist/es2019/pm-plugins/ghost-text-decoration.js +12 -3
  21. package/dist/es2019/pm-plugins/local-slow-lane-client.js +940 -326
  22. package/dist/es2019/pm-plugins/scoring-pipeline.js +137 -13
  23. package/dist/es2019/pm-plugins/slow-lane-client.js +94 -37
  24. package/dist/es2019/pm-plugins/text-predictor.js +1603 -199
  25. package/dist/esm/analytics/ufo.js +2 -1
  26. package/dist/esm/pm-plugins/artifact-loader.js +128 -0
  27. package/dist/esm/pm-plugins/artifacts-manifest.js +3 -0
  28. package/dist/esm/pm-plugins/autocomplete-plugin.js +419 -132
  29. package/dist/esm/pm-plugins/canonical-lm-scoring.js +364 -0
  30. package/dist/esm/pm-plugins/debug-mode.js +89 -6
  31. package/dist/esm/pm-plugins/ghost-text-decoration.js +12 -3
  32. package/dist/esm/pm-plugins/local-slow-lane-client.js +1200 -451
  33. package/dist/esm/pm-plugins/scoring-pipeline.js +163 -18
  34. package/dist/esm/pm-plugins/slow-lane-client.js +100 -37
  35. package/dist/esm/pm-plugins/text-predictor.js +1953 -277
  36. package/dist/types/analytics/ufo.d.ts +1 -0
  37. package/dist/types/pm-plugins/artifact-loader.d.ts +44 -0
  38. package/dist/types/pm-plugins/artifacts-manifest.d.ts +3 -0
  39. package/dist/types/pm-plugins/autocomplete-plugin.d.ts +39 -0
  40. package/dist/types/pm-plugins/canonical-lm-scoring.d.ts +158 -0
  41. package/dist/types/pm-plugins/debug-mode.d.ts +36 -1
  42. package/dist/types/pm-plugins/ghost-text-decoration.d.ts +5 -2
  43. package/dist/types/pm-plugins/local-slow-lane-client.d.ts +87 -54
  44. package/dist/types/pm-plugins/scoring-pipeline.d.ts +33 -1
  45. package/dist/types/pm-plugins/slow-lane-client.d.ts +51 -3
  46. package/dist/types/pm-plugins/text-predictor.d.ts +120 -2
  47. package/package.json +3 -3
  48. package/src/analytics/ufo.ts +5 -0
  49. package/src/pm-plugins/artifact-loader.ts +92 -0
  50. package/src/pm-plugins/artifacts-manifest.ts +3 -0
  51. package/src/pm-plugins/autocomplete-plugin.ts +517 -133
  52. package/src/pm-plugins/canonical-lm-scoring.ts +412 -0
  53. package/src/pm-plugins/debug-mode.ts +109 -7
  54. package/src/pm-plugins/ghost-text-decoration.ts +13 -4
  55. package/src/pm-plugins/local-slow-lane-client.ts +1302 -386
  56. package/src/pm-plugins/scoring-pipeline.ts +167 -17
  57. package/src/pm-plugins/slow-lane-client.ts +147 -62
  58. package/src/pm-plugins/text-predictor.ts +2006 -253
  59. package/CANONICAL_FIX__DO_NOT_USE_ME_A/package.json +0 -8
  60. package/CANONICAL_FIX__DO_NOT_USE_ME_B/package.json +0 -8
  61. package/CANONICAL_FIX__DO_NOT_USE_ME_C/package.json +0 -8
  62. package/dist/cjs/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -18
  63. package/dist/cjs/entry-points/src-pm-plugins-slow-lane-client.js +0 -36
  64. package/dist/cjs/entry-points/src-pm-plugins-text-predictor.js +0 -66
  65. package/dist/es2019/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -2
  66. package/dist/es2019/entry-points/src-pm-plugins-slow-lane-client.js +0 -2
  67. package/dist/es2019/entry-points/src-pm-plugins-text-predictor.js +0 -2
  68. package/dist/esm/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -2
  69. package/dist/esm/entry-points/src-pm-plugins-slow-lane-client.js +0 -2
  70. package/dist/esm/entry-points/src-pm-plugins-text-predictor.js +0 -2
  71. package/dist/types/entry-points/src-pm-plugins-autocomplete-plugin.d.ts +0 -2
  72. package/dist/types/entry-points/src-pm-plugins-slow-lane-client.d.ts +0 -2
  73. package/dist/types/entry-points/src-pm-plugins-text-predictor.d.ts +0 -2
  74. package/scripts/gen_first_token_to_words.py +0 -170
  75. package/src/entry-points/src-pm-plugins-autocomplete-plugin.ts +0 -7
  76. package/src/entry-points/src-pm-plugins-slow-lane-client.ts +0 -13
  77. package/src/entry-points/src-pm-plugins-text-predictor.ts +0 -14
  78. package/src/pm-plugins/autocomplete-plugin/package.json +0 -8
  79. package/src/pm-plugins/slow-lane-client/package.json +0 -8
  80. 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
@@ -386,7 +517,7 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
386
517
  }
387
518
 
388
519
  // eslint-disable-next-line no-console
389
- console.log('%c[Autocomplete] %cgetContext resolved', 'color: #00b8d9; font-weight: bold;', 'color: inherit;', {
520
+ console.log('%c[CTC:signal]%c getContext resolved', CTC_STYLES.brand, CTC_STYLES.body, {
390
521
  source: source,
391
522
  hasParentComment: !!(context !== null && context !== void 0 && context.parentCommentContent),
392
523
  parentCommentPreview: context === null || context === void 0 || (_context$parentCommen = context.parentCommentContent) === null || _context$parentCommen === void 0 ? void 0 : _context$parentCommen.slice(0, 80),
@@ -401,9 +532,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
401
532
  // fullPageContent, so replacing would drop previously resolved context.
402
533
  // Strip undefined values first so a field explicitly set to undefined by
403
534
  // 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];
535
+ var definedContext = Object.fromEntries(Object.entries(context).filter(function (_ref3) {
536
+ var _ref4 = _slicedToArray(_ref3, 2),
537
+ value = _ref4[1];
407
538
  return value !== undefined;
408
539
  }));
409
540
  resolvedContext = _objectSpread(_objectSpread({}, resolvedContext), definedContext);
@@ -449,10 +580,10 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
449
580
  * (no getContext, a request already in flight, or throttled). Callers that
450
581
  * track a retry budget should only count attempts where this returns true.
451
582
  */
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;
583
+ var refreshContext = function refreshContext(_ref5) {
584
+ var source = _ref5.source,
585
+ _ref5$allowThrottle = _ref5.allowThrottle,
586
+ allowThrottle = _ref5$allowThrottle === void 0 ? true : _ref5$allowThrottle;
456
587
  if (!(options !== null && options !== void 0 && options.getContext) || contextRequestInFlight) {
457
588
  return false;
458
589
  }
@@ -483,73 +614,230 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
483
614
  });
484
615
  return true;
485
616
  };
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) {
617
+ var clearDecisionTimers = function clearDecisionTimers() {
493
618
  if (debounceTimer) {
494
619
  clearTimeout(debounceTimer);
620
+ debounceTimer = null;
495
621
  }
496
- debounceTimer = setTimeout(function () {
497
- try {
498
- var state = view.state;
499
- var selection = state.selection;
500
- if (!selection.empty) {
501
- return;
502
- }
622
+ if (decisionDeadlineTimer) {
623
+ clearTimeout(decisionDeadlineTimer);
624
+ decisionDeadlineTimer = null;
625
+ }
626
+ };
627
+ var cancelActiveDecision = function cancelActiveDecision() {
628
+ clearDecisionTimers();
629
+ activeDecision = undefined;
630
+ };
631
+ var isDecisionSnapshotCurrent = function isDecisionSnapshotCurrent(view, decision) {
632
+ var state = view.state;
633
+ return state.selection.empty && state.selection.from === decision.position && getTextBeforeCursor(state) === decision.textBefore;
634
+ };
635
+ var isDecisionCurrent = function isDecisionCurrent(view, decision) {
636
+ var _activeDecision;
637
+ return ((_activeDecision = activeDecision) === null || _activeDecision === void 0 ? void 0 : _activeDecision.revision) === decision.revision && isDecisionSnapshotCurrent(view, decision);
638
+ };
639
+ var expireActiveDecision = function expireActiveDecision(decision) {
640
+ var _activeDecision2;
641
+ if (((_activeDecision2 = activeDecision) === null || _activeDecision2 === void 0 ? void 0 : _activeDecision2.revision) !== decision.revision) {
642
+ return;
643
+ }
644
+ cancelActiveDecision();
645
+ };
646
+ var evaluateActiveDecision = function evaluateActiveDecision(view, revision) {
647
+ var decision = activeDecision;
648
+ if (!decision || decision.revision !== revision || !isDecisionCurrent(view, decision)) {
649
+ return;
650
+ }
651
+ if (performance.now() > decision.deadlineAt) {
652
+ expireActiveDecision(decision);
653
+ return;
654
+ }
655
+ decision.evaluations++;
656
+ try {
657
+ var state = view.state;
658
+ var selection = state.selection;
503
659
 
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);
660
+ // Suppress suggestions when the cursor is mid-word — only offer
661
+ // completions when the cursor is at the trailing edge of a token.
662
+ var nodeAfter = selection.$from.nodeAfter;
663
+ var charAfterCursor = nodeAfter !== null && nodeAfter !== void 0 && nodeAfter.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
664
+ if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
665
+ cancelActiveDecision();
666
+ return;
667
+ }
668
+ if (decision.textBefore === dismissedContext) {
669
+ cancelActiveDecision();
670
+ return;
671
+ }
672
+ dismissedContext = null;
517
673
 
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;
674
+ // Two characters are enough to prefetch the canonical shortlist. The
675
+ // predictor itself withholds display until the three-character gate.
676
+ if (decision.textBefore.trim().length < 2) {
677
+ cancelActiveDecision();
678
+ return;
679
+ }
680
+ var prediction = predict(decision.textBefore);
681
+ if (!prediction || prediction.text.length === 0) {
682
+ // Remain in `collecting` until an async signal arrives or the hard
683
+ // deadline expires. We never display a provisional fallback.
684
+ return;
685
+ }
686
+ var readyAtMs = performance.now() - decision.startedAt;
687
+ if (readyAtMs > GHOST_DECISION_BUDGET_MS) {
688
+ cancelActiveDecision();
689
+ if (isAutocompleteDebugEnabled()) {
690
+ // eslint-disable-next-line no-console
691
+ console.log('%c[CTC:budget]%c fully gated after render deadline (render suppressed)', CTC_STYLES.section, CTC_STYLES.body, {
692
+ revision: decision.revision,
693
+ readyAtMs: readyAtMs,
694
+ surface: prediction.surface
695
+ });
526
696
  }
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
- });
697
+ return;
698
+ }
699
+ var suggestion = showGhostText(view, prediction, decision.position, decision.revision, decision.startedAt);
700
+ if (!suggestion) {
701
+ cancelActiveDecision();
702
+ return;
703
+ }
704
+ cancelActiveDecision();
705
+ if (isAutocompleteDebugEnabled()) {
706
+ // eslint-disable-next-line no-console
707
+ console.log('%c[CTC:decision]%c committed immutable ghost', CTC_STYLES.brand, CTC_STYLES.body, {
708
+ revision: suggestion.revision,
709
+ surface: suggestion.surface,
710
+ ghostText: suggestion.ghostText,
711
+ evidenceTier: suggestion.evidenceTier,
712
+ rankScore: suggestion.rankScore,
713
+ winnerMargin: suggestion.winnerMargin,
714
+ decisionLatencyMs: suggestion.decisionLatencyMs,
715
+ budgetMs: GHOST_DECISION_BUDGET_MS,
716
+ typedPrefixLength: getTrailingSurfacePrefixLength(decision.textBefore),
717
+ evaluations: decision.evaluations,
718
+ asyncSignals: decision.asyncSignals,
719
+ readyWithinBudget: suggestion.decisionLatencyMs <= GHOST_DECISION_BUDGET_MS
720
+ });
721
+ }
722
+ if (suggestion.ghostText !== lastShownGhostText) {
723
+ var _api$analytics5;
724
+ lastShownGhostText = suggestion.ghostText;
725
+ api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 || _api$analytics5.actions.fireAnalyticsEvent({
726
+ action: ACTION.SUGGESTION_VIEWED,
727
+ actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
728
+ eventType: EVENT_TYPE.TRACK,
729
+ attributes: {
730
+ completionSource: getCompletionSource(),
731
+ surface: surface
545
732
  }
546
- }
547
- } catch (error) {
548
- logException(error, {
549
- location: 'editor-plugin-autocomplete/schedulePrediction'
550
733
  });
551
734
  }
552
- }, DEBOUNCE_MS);
735
+ } catch (error) {
736
+ cancelActiveDecision();
737
+ logException(error, {
738
+ location: 'editor-plugin-autocomplete/evaluateActiveDecision'
739
+ });
740
+ }
741
+ };
742
+ var beginPredictionDecision = function beginPredictionDecision(view) {
743
+ cancelActiveDecision();
744
+ var state = view.state;
745
+ if (!state.selection.empty) {
746
+ return;
747
+ }
748
+ var startedAt = performance.now();
749
+ var textBefore = getTextBeforeCursor(state);
750
+ var revision = ++decisionRevision;
751
+ activeDecision = {
752
+ asyncSignals: 0,
753
+ deadlineAt: startedAt + GHOST_DECISION_BUDGET_MS,
754
+ evaluations: 0,
755
+ position: state.selection.from,
756
+ revision: revision,
757
+ startedAt: startedAt,
758
+ textBefore: textBefore
759
+ };
760
+ if (isAutocompleteDebugEnabled()) {
761
+ // eslint-disable-next-line no-console
762
+ console.log('%c[CTC:decision]%c collecting', CTC_STYLES.brand, CTC_STYLES.body, {
763
+ revision: revision,
764
+ budgetMs: GHOST_DECISION_BUDGET_MS,
765
+ coalesceMs: PREDICTION_COALESCE_MS,
766
+ typedPrefixLength: getTrailingSurfacePrefixLength(activeDecision.textBefore),
767
+ textBefore: activeDecision.textBefore.slice(-80)
768
+ });
769
+ }
770
+ debounceTimer = setTimeout(function () {
771
+ debounceTimer = null;
772
+ evaluateActiveDecision(view, revision);
773
+ }, PREDICTION_COALESCE_MS);
774
+ decisionDeadlineTimer = setTimeout(function () {
775
+ var _activeDecision3;
776
+ decisionDeadlineTimer = null;
777
+ if (((_activeDecision3 = activeDecision) === null || _activeDecision3 === void 0 ? void 0 : _activeDecision3.revision) === revision) {
778
+ var expiredDecision = activeDecision;
779
+ if (isAutocompleteDebugEnabled()) {
780
+ var elapsedMs = performance.now() - expiredDecision.startedAt;
781
+ var predictionDebug = getLastPredictionDebug();
782
+ var matchingPredictionDebug = (predictionDebug === null || predictionDebug === void 0 ? void 0 : predictionDebug.textBefore) === expiredDecision.textBefore ? predictionDebug : null;
783
+ var abstentionPrefix = '— abstain: ';
784
+ var predictorDecision = matchingPredictionDebug === null || matchingPredictionDebug === void 0 ? void 0 : matchingPredictionDebug.decision;
785
+ var predictorReason = predictorDecision !== null && predictorDecision !== void 0 && predictorDecision.startsWith(abstentionPrefix) ? predictorDecision.slice(abstentionPrefix.length) : predictorDecision;
786
+ 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) : '');
787
+ // eslint-disable-next-line no-console
788
+ console.log("%c[CTC:decision]%c ".concat(decisionMessage), CTC_STYLES.brand, CTC_STYLES.body, {
789
+ revision: revision,
790
+ budgetMs: GHOST_DECISION_BUDGET_MS,
791
+ elapsedMs: elapsedMs,
792
+ typedPrefixLength: getTrailingSurfacePrefixLength(expiredDecision.textBefore),
793
+ evaluations: expiredDecision.evaluations,
794
+ asyncSignals: expiredDecision.asyncSignals,
795
+ textBefore: expiredDecision.textBefore.slice(-80),
796
+ predictorDecision: predictorReason !== null && predictorReason !== void 0 ? predictorReason : null
797
+ });
798
+ }
799
+ expireActiveDecision(expiredDecision);
800
+ }
801
+ }, GHOST_DECISION_BUDGET_MS);
802
+ };
803
+
804
+ // Async semantic/canonical results may complete the active decision, but they
805
+ // never create a new decision or replace an already committed ghost.
806
+ reschedulePredictionOnSlowLaneResult = function reschedulePredictionOnSlowLaneResult(signal) {
807
+ if (destroyed || !currentView) {
808
+ return;
809
+ }
810
+ if (!activeDecision) {
811
+ return;
812
+ }
813
+ var revision = activeDecision.revision;
814
+ var elapsedMs = performance.now() - activeDecision.startedAt;
815
+ var remainingMs = activeDecision.deadlineAt - performance.now();
816
+ if (remainingMs < 0) {
817
+ var expiredDecision = activeDecision;
818
+ expireActiveDecision(expiredDecision);
819
+ return;
820
+ }
821
+ activeDecision.asyncSignals++;
822
+ if (isAutocompleteDebugVerbose()) {
823
+ // eslint-disable-next-line no-console
824
+ console.log('%c[CTC:readiness]%c async evidence signal', CTC_STYLES.section, CTC_STYLES.body, _objectSpread(_objectSpread({}, signal), {}, {
825
+ revision: revision,
826
+ elapsedMs: elapsedMs,
827
+ remainingMs: Math.max(0, remainingMs),
828
+ typedPrefixLength: getTrailingSurfacePrefixLength(activeDecision.textBefore),
829
+ withinBudget: remainingMs >= 0
830
+ }));
831
+ }
832
+ if (debounceTimer) {
833
+ clearTimeout(debounceTimer);
834
+ }
835
+ debounceTimer = setTimeout(function () {
836
+ debounceTimer = null;
837
+ if (currentView) {
838
+ evaluateActiveDecision(currentView, revision);
839
+ }
840
+ }, 0);
553
841
  };
554
842
  var maybeUpdateSessionFrequency = function maybeUpdateSessionFrequency(view, prevState) {
555
843
  var newText = getTextBeforeCursor(view.state);
@@ -587,20 +875,14 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
587
875
  return _objectSpread(_objectSpread({}, pluginState), meta);
588
876
  }
589
877
 
590
- // A new prediction is scheduled from view.update.
878
+ // A new prediction is scheduled from view.update either way; the
879
+ // surviving ghost only covers the gap until it commits.
591
880
  if (tr.docChanged) {
592
- return _objectSpread(_objectSpread({}, pluginState), {}, {
593
- ghostText: '',
594
- ghostPosition: -1,
595
- decorationSet: DecorationSet.empty
596
- });
881
+ var _advanceGhostThroughT;
882
+ return (_advanceGhostThroughT = advanceGhostThroughTypedCharacter(tr, pluginState)) !== null && _advanceGhostThroughT !== void 0 ? _advanceGhostThroughT : clearGhostState(pluginState);
597
883
  }
598
884
  if (tr.selectionSet && pluginState.ghostText) {
599
- return _objectSpread(_objectSpread({}, pluginState), {}, {
600
- ghostText: '',
601
- ghostPosition: -1,
602
- decorationSet: DecorationSet.empty
603
- });
885
+ return clearGhostState(pluginState);
604
886
  }
605
887
  return pluginState;
606
888
  }
@@ -613,22 +895,25 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
613
895
  },
614
896
  handleKeyDown: keydownHandler({
615
897
  Tab: function Tab(state, dispatch) {
616
- return acceptGhostTextWithAnalytics(state, dispatch, function (ghostText) {
898
+ return acceptGhostTextWithAnalytics(state, dispatch, function (suggestion) {
617
899
  justAccepted = true;
618
900
  lastShownGhostText = '';
619
- fireSuggestionInsertedAnalytics(ghostText);
901
+ cancelActiveDecision();
902
+ fireSuggestionInsertedAnalytics(suggestion);
620
903
  });
621
904
  },
622
905
  ArrowRight: function ArrowRight(state, dispatch) {
623
- return acceptGhostTextWithAnalytics(state, dispatch, function (ghostText) {
906
+ return acceptGhostTextWithAnalytics(state, dispatch, function (suggestion) {
624
907
  justAccepted = true;
625
908
  lastShownGhostText = '';
626
- fireSuggestionInsertedAnalytics(ghostText);
909
+ cancelActiveDecision();
910
+ fireSuggestionInsertedAnalytics(suggestion);
627
911
  });
628
912
  },
629
913
  Escape: function Escape(state, dispatch) {
630
914
  var didClear = clearGhostText(state, dispatch);
631
915
  if (didClear) {
916
+ cancelActiveDecision();
632
917
  dismissedContext = getTextBeforeCursor(state);
633
918
  fireSuggestionDismissedAnalytics('escape');
634
919
  }
@@ -643,6 +928,7 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
643
928
  var pluginState = autocompletePluginKey.getState(view.state);
644
929
  if (pluginState !== null && pluginState !== void 0 && pluginState.ghostText) {
645
930
  clearGhostText(view.state, view.dispatch);
931
+ cancelActiveDecision();
646
932
  fireSuggestionDismissedAnalytics('blur');
647
933
  }
648
934
  return false;
@@ -654,6 +940,7 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
654
940
  if (!event.target.closest('[data-autocomplete-ghost="true"]')) {
655
941
  var didClear = clearGhostText(view.state, view.dispatch);
656
942
  if (didClear) {
943
+ cancelActiveDecision();
657
944
  dismissedContext = getTextBeforeCursor(view.state);
658
945
  lastShownGhostText = '';
659
946
  fireSuggestionDismissedAnalytics('click');
@@ -661,10 +948,11 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
661
948
  return false;
662
949
  }
663
950
  event.preventDefault();
664
- var accepted = acceptGhostTextWithAnalytics(view.state, view.dispatch, function (ghostText) {
951
+ var accepted = acceptGhostTextWithAnalytics(view.state, view.dispatch, function (suggestion) {
665
952
  justAccepted = true;
666
953
  lastShownGhostText = '';
667
- fireSuggestionInsertedAnalytics(ghostText);
954
+ cancelActiveDecision();
955
+ fireSuggestionInsertedAnalytics(suggestion);
668
956
  });
669
957
  if (accepted) {
670
958
  view.focus();
@@ -739,9 +1027,7 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
739
1027
  // Snapshot the post-acceptance text so follow-up transactions hit
740
1028
  // the dismissedContext guard and abort until the user types again.
741
1029
  dismissedContext = getTextBeforeCursor(view.state);
742
- if (debounceTimer) {
743
- clearTimeout(debounceTimer);
744
- }
1030
+ cancelActiveDecision();
745
1031
  return;
746
1032
  }
747
1033
  maybeUpdateSessionFrequency(view, prevState);
@@ -766,7 +1052,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
766
1052
  }
767
1053
  }
768
1054
  }
769
- schedulePrediction(view);
1055
+ beginPredictionDecision(view);
1056
+ } else if (!prevState.selection.eq(view.state.selection)) {
1057
+ cancelActiveDecision();
770
1058
  }
771
1059
  },
772
1060
  destroy: function destroy() {
@@ -775,14 +1063,13 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
775
1063
  currentView = null;
776
1064
  (_unsubscribeFromConte = unsubscribeFromContextUpdates) === null || _unsubscribeFromConte === void 0 || _unsubscribeFromConte();
777
1065
  unsubscribeFromContextUpdates = undefined;
778
- if (debounceTimer) {
779
- clearTimeout(debounceTimer);
780
- }
1066
+ cancelActiveDecision();
1067
+ var releasedDefaultClient = clearDefaultSlowLaneClient(slowLaneClient);
1068
+ 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
1069
  if (hasDestroy(slowLaneClient)) {
782
1070
  slowLaneClient.destroy();
783
1071
  }
784
1072
  ingestedContextTexts.clear();
785
- setDefaultSlowLaneClient(null);
786
1073
  }
787
1074
  };
788
1075
  }