@atlaskit/editor-plugin-autocomplete 2.1.0 → 2.3.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 (37) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/afm-cc/tsconfig.json +3 -1
  3. package/afm-products/tsconfig.json +3 -1
  4. package/build/tsconfig.json +0 -1
  5. package/dist/cjs/autocompletePlugin.js +3 -2
  6. package/dist/cjs/pm-plugins/autocomplete-plugin.js +59 -3
  7. package/dist/cjs/pm-plugins/debug-mode.js +24 -0
  8. package/dist/cjs/pm-plugins/slow-lane-client.js +42 -35
  9. package/dist/cjs/pm-plugins/text-predictor.js +12 -32
  10. package/dist/es2019/autocompletePlugin.js +3 -2
  11. package/dist/es2019/pm-plugins/autocomplete-plugin.js +59 -3
  12. package/dist/es2019/pm-plugins/debug-mode.js +18 -0
  13. package/dist/es2019/pm-plugins/slow-lane-client.js +29 -23
  14. package/dist/es2019/pm-plugins/text-predictor.js +6 -26
  15. package/dist/esm/autocompletePlugin.js +3 -2
  16. package/dist/esm/pm-plugins/autocomplete-plugin.js +59 -3
  17. package/dist/esm/pm-plugins/debug-mode.js +18 -0
  18. package/dist/esm/pm-plugins/slow-lane-client.js +43 -35
  19. package/dist/esm/pm-plugins/text-predictor.js +11 -31
  20. package/dist/types/autocompletePluginType.d.ts +3 -1
  21. package/dist/types/pm-plugins/autocomplete-plugin.d.ts +4 -2
  22. package/dist/types/pm-plugins/debug-mode.d.ts +12 -0
  23. package/dist/types/pm-plugins/text-predictor.d.ts +0 -2
  24. package/dist/types-ts4.5/autocompletePluginType.d.ts +5 -1
  25. package/dist/types-ts4.5/pm-plugins/autocomplete-plugin.d.ts +4 -2
  26. package/dist/types-ts4.5/pm-plugins/debug-mode.d.ts +12 -0
  27. package/dist/types-ts4.5/pm-plugins/text-predictor.d.ts +0 -2
  28. package/package.json +3 -2
  29. package/src/autocompletePlugin.tsx +2 -2
  30. package/src/autocompletePluginType.ts +3 -1
  31. package/src/pm-plugins/autocomplete-plugin.ts +70 -4
  32. package/src/pm-plugins/debug-mode.ts +18 -0
  33. package/src/pm-plugins/slow-lane-client.ts +53 -45
  34. package/src/pm-plugins/text-predictor.ts +12 -34
  35. package/tsconfig.app.json +6 -1
  36. package/tsconfig.dev.json +2 -1
  37. package/afm-jira/tsconfig.json +0 -30
@@ -7,6 +7,8 @@
7
7
  * Response: { semantic_vector: number[], lm_logits: Record<string, number> }
8
8
  */
9
9
 
10
+ import { isAutocompleteDebugEnabled } from './debug-mode';
11
+
10
12
  // ─── Types ───────────────────────────────────────────────────────────────────
11
13
 
12
14
  /** Request payload for typeahead-encodings endpoint. */
@@ -63,16 +65,16 @@ export const createSlowLaneClient = config => {
63
65
  text,
64
66
  session_id: sessionId
65
67
  };
66
-
67
- // Log the exact payload being sent so the comment/reply structure is visible
68
- // eslint-disable-next-line no-console
69
- console.groupCollapsed(`%c[SlowLane] %c📤 Sending context | ${text.length} chars`, 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
70
- text.split('\n').forEach((line, i, arr) => {
68
+ if (isAutocompleteDebugEnabled()) {
69
+ // eslint-disable-next-line no-console
70
+ console.groupCollapsed(`%c[SlowLane] %c📤 Sending context | ${text.length} chars`, 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
71
+ text.split('\n').forEach((line, i, arr) => {
72
+ // eslint-disable-next-line no-console
73
+ console.log(` ${i === arr.length - 1 ? '▶' : ' '} ${line}`);
74
+ });
71
75
  // eslint-disable-next-line no-console
72
- console.log(` ${i === arr.length - 1 ? '▶' : ' '} ${line}`);
73
- });
74
- // eslint-disable-next-line no-console
75
- console.groupEnd();
76
+ console.groupEnd();
77
+ }
76
78
  try {
77
79
  const res = await fetchFn(url, {
78
80
  method: 'POST',
@@ -82,8 +84,10 @@ export const createSlowLaneClient = config => {
82
84
  if (!res.ok) {
83
85
  storedContextVector = null;
84
86
  storedLmLogits = null;
85
- // eslint-disable-next-line no-console
86
- console.log(`%c[SlowLane] %c❌ Request failed (${res.status})`, 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
87
+ if (isAutocompleteDebugEnabled()) {
88
+ // eslint-disable-next-line no-console
89
+ console.log(`%c[SlowLane] %c❌ Request failed (${res.status})`, 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
90
+ }
87
91
  return;
88
92
  }
89
93
  const data = await res.json();
@@ -97,16 +101,16 @@ export const createSlowLaneClient = config => {
97
101
  } else {
98
102
  storedLmLogits = null;
99
103
  }
100
-
101
- // Log what came back so it can be correlated with the next prediction group
102
- // eslint-disable-next-line no-console
103
- console.groupCollapsed(`%c[SlowLane] %c📥 Response received`, 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
104
- // eslint-disable-next-line no-console
105
- console.log(storedContextVector ? `✅ semantic_vector: ${storedContextVector.length} dims` : '❌ No semantic_vector');
106
- // eslint-disable-next-line no-console
107
- console.log(storedLmLogits ? `✅ lm_logits: ${Object.keys(storedLmLogits).length} tokens` : '❌ No lm_logits');
108
- // eslint-disable-next-line no-console
109
- console.groupEnd();
104
+ if (isAutocompleteDebugEnabled()) {
105
+ // eslint-disable-next-line no-console
106
+ console.groupCollapsed(`%c[SlowLane] %c📥 Response received`, 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
107
+ // eslint-disable-next-line no-console
108
+ console.log(storedContextVector ? `✅ semantic_vector: ${storedContextVector.length} dims` : '❌ No semantic_vector');
109
+ // eslint-disable-next-line no-console
110
+ console.log(storedLmLogits ? `✅ lm_logits: ${Object.keys(storedLmLogits).length} tokens` : '❌ No lm_logits');
111
+ // eslint-disable-next-line no-console
112
+ console.groupEnd();
113
+ }
110
114
  onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate({
111
115
  textLength: text.length,
112
116
  hasVector: storedContextVector !== null,
@@ -116,8 +120,10 @@ export const createSlowLaneClient = config => {
116
120
  } catch (e) {
117
121
  storedContextVector = null;
118
122
  storedLmLogits = null;
119
- // eslint-disable-next-line no-console
120
- console.log('%c[SlowLane] %c❌ Network error — context cleared', 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
123
+ if (isAutocompleteDebugEnabled()) {
124
+ // eslint-disable-next-line no-console
125
+ console.log('%c[SlowLane] %c❌ Network error — context cleared', 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
126
+ }
121
127
  }
122
128
  };
123
129
  const updateContextDebounced = text => {
@@ -21,6 +21,7 @@ import l3VocabularyData from './data/l3_vocabulary.json';
21
21
  import vocabularyData from './data/vocabulary_10k.json';
22
22
  import wordIndexData from './data/word_index_10k.json';
23
23
  // import { rankCandidates, isGrammarAllowed } from './scoring-pipeline';
24
+ import { isAutocompleteDebugEnabled } from './debug-mode';
24
25
  import { rankCandidates, STAGE1_WEIGHT, STAGE2_WEIGHT, MIN_STAGE1_SCORE } from './scoring-pipeline';
25
26
  import { getStoredContextVector, getStoredLmLogits } from './slow-lane-client';
26
27
 
@@ -161,7 +162,7 @@ export const initL3Vocabulary = l3Words => {
161
162
  // loses to any domain word in Stage 1, but still scores above 0.
162
163
  l3Trie.insert(word, L3_BASELINE_FREQ, 0, 0);
163
164
  }
164
- if (debugMode) {
165
+ if (isAutocompleteDebugEnabled()) {
165
166
  // eslint-disable-next-line no-console
166
167
  console.log(`[text-predictor] L3 General English loaded: ${l3Words.length} words`);
167
168
  }
@@ -174,9 +175,7 @@ export const initL3Vocabulary = l3Words => {
174
175
  let isInitialized = false;
175
176
  let vectorStore = null;
176
177
  let vectorsLoadStarted = false;
177
- let debugMode = true;
178
178
  let lastPredictionDebug = null;
179
- let hasLoggedSemanticActive = false;
180
179
 
181
180
  /** Get vector for a word from the store. */
182
181
  const getWordVector = word => {
@@ -263,25 +262,12 @@ const extractPreviousWord = text => {
263
262
 
264
263
  // ─── Debug Helpers ───────────────────────────────────────────────────────────
265
264
 
266
- /** Enable or disable debug logging. Also checks localStorage key `autocomplete-debug`. */
267
- export const setDebugMode = enabled => {
268
- debugMode = enabled;
269
- };
270
-
271
- /** Check localStorage for autocomplete-debug on first access. */
272
- const ensureDebugModeFromStorage = () => {
273
- if (typeof localStorage !== 'undefined' && localStorage.getItem('autocomplete-debug') === '1') {
274
- debugMode = true;
275
- }
276
- };
277
-
278
265
  /**
279
266
  * Get predictor status for debugging.
280
267
  * vectorsLoaded: true when semantic scoring is active
281
268
  * wordCount: number of words in vector store (0 if not loaded)
282
269
  */
283
270
  export const getPredictorStatus = () => {
284
- ensureDebugModeFromStorage();
285
271
  return {
286
272
  vectorsLoaded: vectorStore !== null,
287
273
  wordCount: vectorStore ? Object.keys(vectorStore.wordIndex).length : 0,
@@ -295,7 +281,6 @@ export const getPredictorStatus = () => {
295
281
  * Returns null if no prediction has run yet or debug was off.
296
282
  */
297
283
  export const getLastPredictionDebug = () => {
298
- ensureDebugModeFromStorage();
299
284
  return lastPredictionDebug;
300
285
  };
301
286
  export const initVocabulary = vocabulary => {
@@ -336,7 +321,7 @@ export const ingestDocumentPage = pageContent => {
336
321
  validBoostedWords.add(word);
337
322
  }
338
323
  }
339
- if (debugMode && validBoostedWords.size > 0) {
324
+ if (isAutocompleteDebugEnabled() && validBoostedWords.size > 0) {
340
325
  // eslint-disable-next-line no-console
341
326
  console.groupCollapsed(`%c[L1 Session] %cPrimed ${validBoostedWords.size} valid dictionary words from page`, 'color: #00b8d9; font-weight: bold;', 'color: inherit; font-style: italic;');
342
327
  // eslint-disable-next-line no-console
@@ -346,7 +331,6 @@ export const ingestDocumentPage = pageContent => {
346
331
  }
347
332
  };
348
333
  export const predict = textBefore => {
349
- ensureDebugModeFromStorage();
350
334
  if (!isInitialized) {
351
335
  loadDefaultVocabulary();
352
336
  }
@@ -428,7 +412,7 @@ export const predict = textBefore => {
428
412
  const lmLogits = getStoredLmLogits();
429
413
 
430
414
  // Raw LM Output Logger
431
- if (debugMode && lmLogits && Object.keys(lmLogits).length > 0) {
415
+ if (isAutocompleteDebugEnabled() && lmLogits && Object.keys(lmLogits).length > 0) {
432
416
  const rawLmTop = Object.entries(lmLogits).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([word, score]) => ({
433
417
  Word: word,
434
418
  Prob: Number(score.toFixed(5))
@@ -437,9 +421,6 @@ export const predict = textBefore => {
437
421
  console.log('%c[Raw LM Prediction]🧠', 'color: #e83e8c; font-weight: bold;', rawLmTop);
438
422
  }
439
423
  const mode = contextVector ? 'warm' : 'cold';
440
- if (debugMode && contextVector && !hasLoggedSemanticActive) {
441
- hasLoggedSemanticActive = true;
442
- }
443
424
 
444
425
  // Build ScoringCandidate array from TrieNodes
445
426
  const scoringCandidates = candidates.map(({
@@ -464,7 +445,7 @@ export const predict = textBefore => {
464
445
  } = rankCandidates(scoringCandidates, contextVector, w => getWordVector(w), prefixLmLogits, wordTrie.maxTenantFreq, previousWord);
465
446
  const best = ranked[0];
466
447
  const suggestion = best && best.finalScore >= MIN_SCORE_THRESHOLD ? best.word.slice(currentWord.length) : null;
467
- if (debugMode) {
448
+ if (isAutocompleteDebugEnabled()) {
468
449
  const latencyMs = performance.now() - t0;
469
450
  const tokens = tokenize(trimmed);
470
451
  const contextWords = tokens.slice(-CONTEXT_WORDS);
@@ -612,8 +593,7 @@ export const loadVectorsAsync = async options => {
612
593
  wordIndex,
613
594
  dim
614
595
  };
615
- ensureDebugModeFromStorage();
616
- if (debugMode) {
596
+ if (isAutocompleteDebugEnabled()) {
617
597
  // eslint-disable-next-line no-console
618
598
  console.log('[text-predictor] Vectors loaded:', {
619
599
  wordCount: nWords,
@@ -1,6 +1,7 @@
1
1
  import { autocompletePluginKey, createAutocompletePlugin } from './pm-plugins/autocomplete-plugin';
2
2
  export var autocompletePlugin = function autocompletePlugin(_ref) {
3
- var options = _ref.config;
3
+ var options = _ref.config,
4
+ api = _ref.api;
4
5
  return {
5
6
  name: 'autocomplete',
6
7
  getSharedState: function getSharedState(editorState) {
@@ -13,7 +14,7 @@ export var autocompletePlugin = function autocompletePlugin(_ref) {
13
14
  return [{
14
15
  name: 'autocomplete',
15
16
  plugin: function plugin() {
16
- return createAutocompletePlugin(options);
17
+ return createAutocompletePlugin(options, api);
17
18
  }
18
19
  }];
19
20
  }
@@ -1,6 +1,7 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
3
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
4
5
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
5
6
  import { keydownHandler } from '@atlaskit/editor-prosemirror/keymap';
6
7
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
@@ -61,6 +62,7 @@ var setAutocompleteMeta = function setAutocompleteMeta(tr, meta) {
61
62
  /**
62
63
  * Apply a ghost text suggestion to the editor state.
63
64
  */
65
+ var lastShownGhostText = '';
64
66
  var showGhostText = function showGhostText(view, text, position) {
65
67
  var state = view.state,
66
68
  dispatch = view.dispatch;
@@ -139,7 +141,17 @@ var buildSlowLaneText = function buildSlowLaneText(docText, context) {
139
141
  lines.push("reply ".concat(nextReplyNumber, ": ").concat(docText));
140
142
  return lines.join('\n');
141
143
  };
142
- export var createAutocompletePlugin = function createAutocompletePlugin(options) {
144
+ var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefore) {
145
+ var _textBefore$match$, _textBefore$match;
146
+ if (isWordBoundary(textBefore)) {
147
+ return 0;
148
+ }
149
+
150
+ // eslint-disable-next-line require-unicode-regexp
151
+ var trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(/([^\s.,;:!?]*)$/)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
152
+ return trailingToken.length;
153
+ };
154
+ export var createAutocompletePlugin = function createAutocompletePlugin(options, api) {
143
155
  var debounceTimer = null;
144
156
  var hasIngestedPage = false;
145
157
  var resolvedContext;
@@ -156,6 +168,24 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options)
156
168
  * re-showing the same suggestion. Resets to null as soon as the text changes.
157
169
  */
158
170
  var dismissedContext = null;
171
+ var lastSuggestionTypedLength = 0;
172
+ var lastSuggestionLength = 0;
173
+ var fireSuggestionInsertedAnalytics = function fireSuggestionInsertedAnalytics(ghostText) {
174
+ var _api$analytics;
175
+ var typedLength = lastSuggestionTypedLength;
176
+ var suggestionLength = lastSuggestionLength || typedLength + ghostText.length;
177
+ var kssDelta = suggestionLength - typedLength;
178
+ api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.fireAnalyticsEvent({
179
+ action: ACTION.SUGGESTION_INSERTED,
180
+ actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
181
+ eventType: EVENT_TYPE.TRACK,
182
+ attributes: {
183
+ suggestionLength: suggestionLength,
184
+ typedLength: typedLength,
185
+ kssDelta: kssDelta
186
+ }
187
+ });
188
+ };
159
189
  var slowLaneClient = createSlowLaneClient({
160
190
  baseUrl: '',
161
191
  endpoint: SLOW_LANE_ENDPOINT
@@ -196,7 +226,19 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options)
196
226
  // Tier 1 prediction is synchronous -- no async needed
197
227
  var prediction = predict(textBefore);
198
228
  if (prediction && prediction.length > 0) {
229
+ var typedLength = getTypedLengthForPrediction(textBefore);
230
+ lastSuggestionTypedLength = typedLength;
231
+ lastSuggestionLength = typedLength + prediction.length;
199
232
  showGhostText(view, prediction, selection.from);
233
+ if (prediction !== lastShownGhostText) {
234
+ var _api$analytics2;
235
+ lastShownGhostText = prediction;
236
+ api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || _api$analytics2.actions.fireAnalyticsEvent({
237
+ action: ACTION.SUGGESTION_VIEWED,
238
+ actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
239
+ eventType: EVENT_TYPE.TRACK
240
+ });
241
+ }
200
242
  }
201
243
  }, DEBOUNCE_MS);
202
244
  };
@@ -267,13 +309,27 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options)
267
309
  },
268
310
  handleKeyDown: keydownHandler({
269
311
  Tab: function Tab(state, dispatch) {
312
+ var _pluginState$ghostTex;
313
+ var pluginState = autocompletePluginKey.getState(state);
314
+ var ghostText = (_pluginState$ghostTex = pluginState === null || pluginState === void 0 ? void 0 : pluginState.ghostText) !== null && _pluginState$ghostTex !== void 0 ? _pluginState$ghostTex : '';
270
315
  var accepted = acceptGhostText(state, dispatch);
271
- if (accepted) justAccepted = true;
316
+ if (accepted) {
317
+ justAccepted = true;
318
+ lastShownGhostText = '';
319
+ fireSuggestionInsertedAnalytics(ghostText);
320
+ }
272
321
  return accepted;
273
322
  },
274
323
  ArrowRight: function ArrowRight(state, dispatch) {
324
+ var _pluginState$ghostTex2;
325
+ var pluginState = autocompletePluginKey.getState(state);
326
+ var ghostText = (_pluginState$ghostTex2 = pluginState === null || pluginState === void 0 ? void 0 : pluginState.ghostText) !== null && _pluginState$ghostTex2 !== void 0 ? _pluginState$ghostTex2 : '';
275
327
  var accepted = acceptGhostText(state, dispatch);
276
- if (accepted) justAccepted = true;
328
+ if (accepted) {
329
+ justAccepted = true;
330
+ lastShownGhostText = '';
331
+ fireSuggestionInsertedAnalytics(ghostText);
332
+ }
277
333
  return accepted;
278
334
  },
279
335
  Escape: function Escape(state, dispatch) {
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Contextual Typeahead Completions (CTC) debug logging utility.
3
+ *
4
+ * Logs are silent by default. To enable in any environment (dev, staging, prod):
5
+ *
6
+ * localStorage.setItem('atl-ctc-dbg', '1')
7
+ *
8
+ * Then reload the page. To disable:
9
+ *
10
+ * localStorage.removeItem('atl-ctc-dbg')
11
+ */
12
+ export var isAutocompleteDebugEnabled = function isAutocompleteDebugEnabled() {
13
+ try {
14
+ return typeof localStorage !== 'undefined' && localStorage.getItem('atl-ctc-dbg') === '1';
15
+ } catch (_unused) {
16
+ return false;
17
+ }
18
+ };
@@ -10,6 +10,8 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
10
10
  * Response: { semantic_vector: number[], lm_logits: Record<string, number> }
11
11
  */
12
12
 
13
+ import { isAutocompleteDebugEnabled } from './debug-mode';
14
+
13
15
  // ─── Types ───────────────────────────────────────────────────────────────────
14
16
 
15
17
  /** Request payload for typeahead-encodings endpoint. */
@@ -74,37 +76,41 @@ export var createSlowLaneClient = function createSlowLaneClient(config) {
74
76
  payload = {
75
77
  text: text,
76
78
  session_id: sessionId
77
- }; // Log the exact payload being sent so the comment/reply structure is visible
78
- // eslint-disable-next-line no-console
79
- console.groupCollapsed("%c[SlowLane] %c\uD83D\uDCE4 Sending context | ".concat(text.length, " chars"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
80
- text.split('\n').forEach(function (line, i, arr) {
79
+ };
80
+ if (isAutocompleteDebugEnabled()) {
81
81
  // eslint-disable-next-line no-console
82
- console.log(" ".concat(i === arr.length - 1 ? '▶' : ' ', " ").concat(line));
83
- });
84
- // eslint-disable-next-line no-console
85
- console.groupEnd();
86
- _context.prev = 7;
87
- _context.next = 10;
82
+ console.groupCollapsed("%c[SlowLane] %c\uD83D\uDCE4 Sending context | ".concat(text.length, " chars"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
83
+ text.split('\n').forEach(function (line, i, arr) {
84
+ // eslint-disable-next-line no-console
85
+ console.log(" ".concat(i === arr.length - 1 ? '▶' : ' ', " ").concat(line));
86
+ });
87
+ // eslint-disable-next-line no-console
88
+ console.groupEnd();
89
+ }
90
+ _context.prev = 5;
91
+ _context.next = 8;
88
92
  return fetchFn(url, {
89
93
  method: 'POST',
90
94
  headers: headers,
91
95
  body: JSON.stringify(payload)
92
96
  });
93
- case 10:
97
+ case 8:
94
98
  res = _context.sent;
95
99
  if (res.ok) {
96
- _context.next = 16;
100
+ _context.next = 14;
97
101
  break;
98
102
  }
99
103
  storedContextVector = null;
100
104
  storedLmLogits = null;
101
- // eslint-disable-next-line no-console
102
- console.log("%c[SlowLane] %c\u274C Request failed (".concat(res.status, ")"), 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
105
+ if (isAutocompleteDebugEnabled()) {
106
+ // eslint-disable-next-line no-console
107
+ console.log("%c[SlowLane] %c\u274C Request failed (".concat(res.status, ")"), 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
108
+ }
103
109
  return _context.abrupt("return");
104
- case 16:
105
- _context.next = 18;
110
+ case 14:
111
+ _context.next = 16;
106
112
  return res.json();
107
- case 18:
113
+ case 16:
108
114
  data = _context.sent;
109
115
  if (data.semantic_vector && Array.isArray(data.semantic_vector)) {
110
116
  storedContextVector = new Float32Array(data.semantic_vector);
@@ -116,36 +122,38 @@ export var createSlowLaneClient = function createSlowLaneClient(config) {
116
122
  } else {
117
123
  storedLmLogits = null;
118
124
  }
119
-
120
- // Log what came back so it can be correlated with the next prediction group
121
- // eslint-disable-next-line no-console
122
- console.groupCollapsed("%c[SlowLane] %c\uD83D\uDCE5 Response received", 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
123
- // eslint-disable-next-line no-console
124
- console.log(storedContextVector ? "\u2705 semantic_vector: ".concat(storedContextVector.length, " dims") : '❌ No semantic_vector');
125
- // eslint-disable-next-line no-console
126
- console.log(storedLmLogits ? "\u2705 lm_logits: ".concat(Object.keys(storedLmLogits).length, " tokens") : '❌ No lm_logits');
127
- // eslint-disable-next-line no-console
128
- console.groupEnd();
125
+ if (isAutocompleteDebugEnabled()) {
126
+ // eslint-disable-next-line no-console
127
+ console.groupCollapsed("%c[SlowLane] %c\uD83D\uDCE5 Response received", 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
128
+ // eslint-disable-next-line no-console
129
+ console.log(storedContextVector ? "\u2705 semantic_vector: ".concat(storedContextVector.length, " dims") : '❌ No semantic_vector');
130
+ // eslint-disable-next-line no-console
131
+ console.log(storedLmLogits ? "\u2705 lm_logits: ".concat(Object.keys(storedLmLogits).length, " tokens") : '❌ No lm_logits');
132
+ // eslint-disable-next-line no-console
133
+ console.groupEnd();
134
+ }
129
135
  onUpdate === null || onUpdate === void 0 || onUpdate({
130
136
  textLength: text.length,
131
137
  hasVector: storedContextVector !== null,
132
138
  hasLmLogits: storedLmLogits !== null
133
139
  });
134
140
  // eslint-disable-next-line no-unused-vars
135
- _context.next = 33;
141
+ _context.next = 28;
136
142
  break;
137
- case 28:
138
- _context.prev = 28;
139
- _context.t0 = _context["catch"](7);
143
+ case 23:
144
+ _context.prev = 23;
145
+ _context.t0 = _context["catch"](5);
140
146
  storedContextVector = null;
141
147
  storedLmLogits = null;
142
- // eslint-disable-next-line no-console
143
- console.log('%c[SlowLane] %c❌ Network error — context cleared', 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
144
- case 33:
148
+ if (isAutocompleteDebugEnabled()) {
149
+ // eslint-disable-next-line no-console
150
+ console.log('%c[SlowLane] %c❌ Network error — context cleared', 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
151
+ }
152
+ case 28:
145
153
  case "end":
146
154
  return _context.stop();
147
155
  }
148
- }, _callee, null, [[7, 28]]);
156
+ }, _callee, null, [[5, 23]]);
149
157
  }));
150
158
  return function doUpdateContext(_x) {
151
159
  return _ref.apply(this, arguments);
@@ -29,6 +29,7 @@ import l3VocabularyData from './data/l3_vocabulary.json';
29
29
  import vocabularyData from './data/vocabulary_10k.json';
30
30
  import wordIndexData from './data/word_index_10k.json';
31
31
  // import { rankCandidates, isGrammarAllowed } from './scoring-pipeline';
32
+ import { isAutocompleteDebugEnabled } from './debug-mode';
32
33
  import { rankCandidates, STAGE1_WEIGHT, STAGE2_WEIGHT, MIN_STAGE1_SCORE } from './scoring-pipeline';
33
34
  import { getStoredContextVector, getStoredLmLogits } from './slow-lane-client';
34
35
 
@@ -223,7 +224,7 @@ export var initL3Vocabulary = function initL3Vocabulary(l3Words) {
223
224
  } finally {
224
225
  _iterator5.f();
225
226
  }
226
- if (debugMode) {
227
+ if (isAutocompleteDebugEnabled()) {
227
228
  // eslint-disable-next-line no-console
228
229
  console.log("[text-predictor] L3 General English loaded: ".concat(l3Words.length, " words"));
229
230
  }
@@ -236,9 +237,7 @@ export var initL3Vocabulary = function initL3Vocabulary(l3Words) {
236
237
  var isInitialized = false;
237
238
  var vectorStore = null;
238
239
  var vectorsLoadStarted = false;
239
- var debugMode = true;
240
240
  var lastPredictionDebug = null;
241
- var hasLoggedSemanticActive = false;
242
241
 
243
242
  /** Get vector for a word from the store. */
244
243
  var getWordVector = function getWordVector(word) {
@@ -344,25 +343,12 @@ var extractPreviousWord = function extractPreviousWord(text) {
344
343
 
345
344
  // ─── Debug Helpers ───────────────────────────────────────────────────────────
346
345
 
347
- /** Enable or disable debug logging. Also checks localStorage key `autocomplete-debug`. */
348
- export var setDebugMode = function setDebugMode(enabled) {
349
- debugMode = enabled;
350
- };
351
-
352
- /** Check localStorage for autocomplete-debug on first access. */
353
- var ensureDebugModeFromStorage = function ensureDebugModeFromStorage() {
354
- if (typeof localStorage !== 'undefined' && localStorage.getItem('autocomplete-debug') === '1') {
355
- debugMode = true;
356
- }
357
- };
358
-
359
346
  /**
360
347
  * Get predictor status for debugging.
361
348
  * vectorsLoaded: true when semantic scoring is active
362
349
  * wordCount: number of words in vector store (0 if not loaded)
363
350
  */
364
351
  export var getPredictorStatus = function getPredictorStatus() {
365
- ensureDebugModeFromStorage();
366
352
  return {
367
353
  vectorsLoaded: vectorStore !== null,
368
354
  wordCount: vectorStore ? Object.keys(vectorStore.wordIndex).length : 0,
@@ -376,7 +362,6 @@ export var getPredictorStatus = function getPredictorStatus() {
376
362
  * Returns null if no prediction has run yet or debug was off.
377
363
  */
378
364
  export var getLastPredictionDebug = function getLastPredictionDebug() {
379
- ensureDebugModeFromStorage();
380
365
  return lastPredictionDebug;
381
366
  };
382
367
  export var initVocabulary = function initVocabulary(vocabulary) {
@@ -435,7 +420,7 @@ export var ingestDocumentPage = function ingestDocumentPage(pageContent) {
435
420
  } finally {
436
421
  _iterator9.f();
437
422
  }
438
- if (debugMode && validBoostedWords.size > 0) {
423
+ if (isAutocompleteDebugEnabled() && validBoostedWords.size > 0) {
439
424
  // eslint-disable-next-line no-console
440
425
  console.groupCollapsed("%c[L1 Session] %cPrimed ".concat(validBoostedWords.size, " valid dictionary words from page"), 'color: #00b8d9; font-weight: bold;', 'color: inherit; font-style: italic;');
441
426
  // eslint-disable-next-line no-console
@@ -445,7 +430,6 @@ export var ingestDocumentPage = function ingestDocumentPage(pageContent) {
445
430
  }
446
431
  };
447
432
  export var predict = function predict(textBefore) {
448
- ensureDebugModeFromStorage();
449
433
  if (!isInitialized) {
450
434
  loadDefaultVocabulary();
451
435
  }
@@ -538,7 +522,7 @@ export var predict = function predict(textBefore) {
538
522
  var lmLogits = getStoredLmLogits();
539
523
 
540
524
  // Raw LM Output Logger
541
- if (debugMode && lmLogits && Object.keys(lmLogits).length > 0) {
525
+ if (isAutocompleteDebugEnabled() && lmLogits && Object.keys(lmLogits).length > 0) {
542
526
  var rawLmTop = Object.entries(lmLogits).sort(function (a, b) {
543
527
  return b[1] - a[1];
544
528
  }).slice(0, 5).map(function (_ref) {
@@ -554,9 +538,6 @@ export var predict = function predict(textBefore) {
554
538
  console.log('%c[Raw LM Prediction]🧠', 'color: #e83e8c; font-weight: bold;', rawLmTop);
555
539
  }
556
540
  var mode = contextVector ? 'warm' : 'cold';
557
- if (debugMode && contextVector && !hasLoggedSemanticActive) {
558
- hasLoggedSemanticActive = true;
559
- }
560
541
 
561
542
  // Build ScoringCandidate array from TrieNodes
562
543
  var scoringCandidates = candidates.map(function (_ref3) {
@@ -587,7 +568,7 @@ export var predict = function predict(textBefore) {
587
568
  pipelineDebug = _rankCandidates.pipelineDebug;
588
569
  var best = ranked[0];
589
570
  var suggestion = best && best.finalScore >= MIN_SCORE_THRESHOLD ? best.word.slice(currentWord.length) : null;
590
- if (debugMode) {
571
+ if (isAutocompleteDebugEnabled()) {
591
572
  var latencyMs = performance.now() - t0;
592
573
  var tokens = tokenize(trimmed);
593
574
  var contextWords = tokens.slice(-CONTEXT_WORDS);
@@ -767,8 +748,7 @@ export var loadVectorsAsync = /*#__PURE__*/function () {
767
748
  wordIndex: wordIndex,
768
749
  dim: dim
769
750
  };
770
- ensureDebugModeFromStorage();
771
- if (debugMode) {
751
+ if (isAutocompleteDebugEnabled()) {
772
752
  // eslint-disable-next-line no-console
773
753
  console.log('[text-predictor] Vectors loaded:', {
774
754
  wordCount: nWords,
@@ -776,19 +756,19 @@ export var loadVectorsAsync = /*#__PURE__*/function () {
776
756
  sizeBytes: float32.byteLength
777
757
  });
778
758
  }
779
- _context.next = 41;
759
+ _context.next = 40;
780
760
  break;
781
- case 37:
782
- _context.prev = 37;
761
+ case 36:
762
+ _context.prev = 36;
783
763
  _context.t1 = _context["catch"](17);
784
764
  vectorsLoadStarted = false;
785
765
  // eslint-disable-next-line no-console
786
766
  console.warn('[text-predictor] Failed to load vectors:', _context.t1);
787
- case 41:
767
+ case 40:
788
768
  case "end":
789
769
  return _context.stop();
790
770
  }
791
- }, _callee, null, [[6, 12], [17, 37]]);
771
+ }, _callee, null, [[6, 12], [17, 36]]);
792
772
  }));
793
773
  return function loadVectorsAsync(_x) {
794
774
  return _ref6.apply(this, arguments);
@@ -1,6 +1,8 @@
1
- import type { NextEditorPlugin } from '@atlaskit/editor-common/types';
1
+ import type { NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
2
+ import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
2
3
  import type { AutocompletePluginOptions, AutocompletePluginState } from './pm-plugins/autocomplete-plugin';
3
4
  export type AutocompletePlugin = NextEditorPlugin<'autocomplete', {
4
5
  pluginConfiguration?: AutocompletePluginOptions | undefined;
5
6
  sharedState: AutocompletePluginState | undefined;
7
+ dependencies: [OptionalPlugin<AnalyticsPlugin>];
6
8
  }>;
@@ -1,7 +1,9 @@
1
1
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
3
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
3
4
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
4
- export declare const autocompletePluginKey: PluginKey<any>;
5
+ import type { AutocompletePlugin } from '../autocompletePluginType';
6
+ export declare const autocompletePluginKey: PluginKey;
5
7
  export interface AutocompletePluginState {
6
8
  /** The decoration set containing the ghost text widget */
7
9
  decorationSet: DecorationSet;
@@ -39,4 +41,4 @@ export interface AutocompletePluginOptions {
39
41
  */
40
42
  getVectorsBinaryUrl?: () => Promise<string>;
41
43
  }
42
- export declare const createAutocompletePlugin: (options?: AutocompletePluginOptions) => SafePlugin<AutocompletePluginState>;
44
+ export declare const createAutocompletePlugin: (options?: AutocompletePluginOptions, api?: ExtractInjectionAPI<AutocompletePlugin>) => SafePlugin<AutocompletePluginState>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Contextual Typeahead Completions (CTC) debug logging utility.
3
+ *
4
+ * Logs are silent by default. To enable in any environment (dev, staging, prod):
5
+ *
6
+ * localStorage.setItem('atl-ctc-dbg', '1')
7
+ *
8
+ * Then reload the page. To disable:
9
+ *
10
+ * localStorage.removeItem('atl-ctc-dbg')
11
+ */
12
+ export declare const isAutocompleteDebugEnabled: () => boolean;