@atlaskit/editor-plugin-autocomplete 2.3.0 → 2.5.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.
- package/CHANGELOG.md +42 -0
- package/afm-cc/tsconfig.json +3 -0
- package/afm-products/tsconfig.json +3 -0
- package/autocompletePlugin/package.json +15 -0
- package/autocompletePluginType/package.json +15 -0
- package/dist/cjs/analytics/ufo.js +111 -0
- package/dist/cjs/entry-points/autocompletePlugin.js +12 -0
- package/dist/cjs/entry-points/autocompletePluginType.js +1 -0
- package/dist/cjs/entry-points/src-pm-plugins-autocomplete-plugin.js +18 -0
- package/dist/cjs/entry-points/src-pm-plugins-slow-lane-client.js +36 -0
- package/dist/cjs/entry-points/src-pm-plugins-text-predictor.js +66 -0
- package/dist/cjs/pm-plugins/autocomplete-plugin.js +126 -64
- package/dist/cjs/pm-plugins/local-slow-lane-client.js +447 -0
- package/dist/cjs/pm-plugins/slow-lane-client.js +45 -16
- package/dist/cjs/pm-plugins/text-predictor.js +72 -40
- package/dist/es2019/analytics/ufo.js +110 -0
- package/dist/es2019/entry-points/autocompletePlugin.js +2 -0
- package/dist/es2019/entry-points/autocompletePluginType.js +0 -0
- package/dist/es2019/entry-points/src-pm-plugins-autocomplete-plugin.js +2 -0
- package/dist/es2019/entry-points/src-pm-plugins-slow-lane-client.js +2 -0
- package/dist/es2019/entry-points/src-pm-plugins-text-predictor.js +2 -0
- package/dist/es2019/pm-plugins/autocomplete-plugin.js +132 -72
- package/dist/es2019/pm-plugins/local-slow-lane-client.js +355 -0
- package/dist/es2019/pm-plugins/slow-lane-client.js +29 -2
- package/dist/es2019/pm-plugins/text-predictor.js +48 -15
- package/dist/esm/analytics/ufo.js +105 -0
- package/dist/esm/entry-points/autocompletePlugin.js +2 -0
- package/dist/esm/entry-points/autocompletePluginType.js +0 -0
- package/dist/esm/entry-points/src-pm-plugins-autocomplete-plugin.js +2 -0
- package/dist/esm/entry-points/src-pm-plugins-slow-lane-client.js +2 -0
- package/dist/esm/entry-points/src-pm-plugins-text-predictor.js +2 -0
- package/dist/esm/pm-plugins/autocomplete-plugin.js +126 -64
- package/dist/esm/pm-plugins/local-slow-lane-client.js +439 -0
- package/dist/esm/pm-plugins/slow-lane-client.js +45 -16
- package/dist/esm/pm-plugins/text-predictor.js +73 -40
- package/dist/types/analytics/ufo.d.ts +38 -0
- package/dist/types/entry-points/autocompletePlugin.d.ts +1 -0
- package/dist/types/entry-points/autocompletePluginType.d.ts +1 -0
- package/dist/types/entry-points/src-pm-plugins-autocomplete-plugin.d.ts +2 -0
- package/dist/types/entry-points/src-pm-plugins-slow-lane-client.d.ts +2 -0
- package/dist/types/entry-points/src-pm-plugins-text-predictor.d.ts +2 -0
- package/dist/types/pm-plugins/autocomplete-plugin.d.ts +5 -0
- package/dist/types/pm-plugins/local-slow-lane-client.d.ts +102 -0
- package/dist/types-ts4.5/analytics/ufo.d.ts +38 -0
- package/dist/types-ts4.5/entry-points/autocompletePlugin.d.ts +1 -0
- package/dist/types-ts4.5/entry-points/autocompletePluginType.d.ts +1 -0
- package/dist/types-ts4.5/entry-points/src-pm-plugins-autocomplete-plugin.d.ts +2 -0
- package/dist/types-ts4.5/entry-points/src-pm-plugins-slow-lane-client.d.ts +2 -0
- package/dist/types-ts4.5/entry-points/src-pm-plugins-text-predictor.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/autocomplete-plugin.d.ts +5 -0
- package/dist/types-ts4.5/pm-plugins/local-slow-lane-client.d.ts +102 -0
- package/package.json +8 -3
- package/src/analytics/ufo.ts +139 -0
- package/src/entry-points/autocompletePlugin.tsx +2 -0
- package/src/entry-points/autocompletePluginType.ts +2 -0
- package/src/entry-points/src-pm-plugins-autocomplete-plugin.ts +7 -0
- package/src/entry-points/src-pm-plugins-slow-lane-client.ts +13 -0
- package/src/entry-points/src-pm-plugins-text-predictor.ts +14 -0
- package/src/pm-plugins/autocomplete-plugin/package.json +5 -5
- package/src/pm-plugins/autocomplete-plugin.ts +126 -64
- package/src/pm-plugins/local-slow-lane-client.ts +480 -0
- package/src/pm-plugins/slow-lane-client/package.json +5 -5
- package/src/pm-plugins/slow-lane-client.ts +28 -2
- package/src/pm-plugins/text-predictor/package.json +5 -5
- package/src/pm-plugins/text-predictor.ts +42 -12
- package/tsconfig.app.json +3 -0
|
@@ -11,6 +11,7 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
|
|
|
11
11
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
12
12
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
13
13
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
14
|
+
var _ufo = require("../analytics/ufo");
|
|
14
15
|
var _l3_vocabulary = _interopRequireDefault(require("./data/l3_vocabulary.json"));
|
|
15
16
|
var _vocabulary_10k = _interopRequireDefault(require("./data/vocabulary_10k.json"));
|
|
16
17
|
var _word_index_10k = _interopRequireDefault(require("./data/word_index_10k.json"));
|
|
@@ -709,38 +710,46 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
|
|
|
709
710
|
return _context.abrupt("return");
|
|
710
711
|
case 5:
|
|
711
712
|
vectorsLoadStarted = true;
|
|
712
|
-
|
|
713
|
-
_context.
|
|
713
|
+
(0, _ufo.startExp)(_ufo.EXPERIENCE_NAME.LOAD_VECTORS, 'singleton');
|
|
714
|
+
_context.prev = 7;
|
|
715
|
+
_context.next = 10;
|
|
714
716
|
return options.getBinaryUrl();
|
|
715
|
-
case
|
|
717
|
+
case 10:
|
|
716
718
|
url = _context.sent;
|
|
717
|
-
_context.next =
|
|
719
|
+
_context.next = 19;
|
|
718
720
|
break;
|
|
719
|
-
case
|
|
720
|
-
_context.prev =
|
|
721
|
-
_context.t0 = _context["catch"](
|
|
721
|
+
case 13:
|
|
722
|
+
_context.prev = 13;
|
|
723
|
+
_context.t0 = _context["catch"](7);
|
|
722
724
|
vectorsLoadStarted = false;
|
|
725
|
+
(0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.LOAD_VECTORS, 'singleton', {
|
|
726
|
+
errorType: 'resolve_url'
|
|
727
|
+
});
|
|
723
728
|
// eslint-disable-next-line no-console
|
|
724
729
|
console.warn('[text-predictor] Failed to resolve vectors URL:', _context.t0);
|
|
725
730
|
return _context.abrupt("return");
|
|
726
|
-
case
|
|
727
|
-
_context.prev =
|
|
728
|
-
_context.next =
|
|
731
|
+
case 19:
|
|
732
|
+
_context.prev = 19;
|
|
733
|
+
_context.next = 22;
|
|
729
734
|
return fetch(url);
|
|
730
|
-
case
|
|
735
|
+
case 22:
|
|
731
736
|
res = _context.sent;
|
|
732
737
|
if (res.ok) {
|
|
733
|
-
_context.next =
|
|
738
|
+
_context.next = 28;
|
|
734
739
|
break;
|
|
735
740
|
}
|
|
736
741
|
vectorsLoadStarted = false;
|
|
742
|
+
(0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.LOAD_VECTORS, 'singleton', {
|
|
743
|
+
status: res.status,
|
|
744
|
+
errorType: 'http_error'
|
|
745
|
+
});
|
|
737
746
|
// eslint-disable-next-line no-console
|
|
738
747
|
console.warn("[text-predictor] Failed to load vectors: ".concat(res.status));
|
|
739
748
|
return _context.abrupt("return");
|
|
740
|
-
case
|
|
741
|
-
_context.next =
|
|
749
|
+
case 28:
|
|
750
|
+
_context.next = 30;
|
|
742
751
|
return res.arrayBuffer();
|
|
743
|
-
case
|
|
752
|
+
case 30:
|
|
744
753
|
buffer = _context.sent;
|
|
745
754
|
float32 = new Float32Array(buffer);
|
|
746
755
|
wordIndex = _word_index_10k.default;
|
|
@@ -751,6 +760,11 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
|
|
|
751
760
|
wordIndex: wordIndex,
|
|
752
761
|
dim: dim
|
|
753
762
|
};
|
|
763
|
+
(0, _ufo.succeedExp)(_ufo.EXPERIENCE_NAME.LOAD_VECTORS, 'singleton', {
|
|
764
|
+
wordCount: nWords,
|
|
765
|
+
dim: dim,
|
|
766
|
+
sizeBytes: float32.byteLength
|
|
767
|
+
});
|
|
754
768
|
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
755
769
|
// eslint-disable-next-line no-console
|
|
756
770
|
console.log('[text-predictor] Vectors loaded:', {
|
|
@@ -759,19 +773,22 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
|
|
|
759
773
|
sizeBytes: float32.byteLength
|
|
760
774
|
});
|
|
761
775
|
}
|
|
762
|
-
_context.next =
|
|
776
|
+
_context.next = 45;
|
|
763
777
|
break;
|
|
764
|
-
case
|
|
765
|
-
_context.prev =
|
|
766
|
-
_context.t1 = _context["catch"](
|
|
778
|
+
case 40:
|
|
779
|
+
_context.prev = 40;
|
|
780
|
+
_context.t1 = _context["catch"](19);
|
|
767
781
|
vectorsLoadStarted = false;
|
|
782
|
+
(0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.LOAD_VECTORS, 'singleton', {
|
|
783
|
+
errorType: 'network'
|
|
784
|
+
});
|
|
768
785
|
// eslint-disable-next-line no-console
|
|
769
786
|
console.warn('[text-predictor] Failed to load vectors:', _context.t1);
|
|
770
|
-
case
|
|
787
|
+
case 45:
|
|
771
788
|
case "end":
|
|
772
789
|
return _context.stop();
|
|
773
790
|
}
|
|
774
|
-
}, _callee, null, [[
|
|
791
|
+
}, _callee, null, [[7, 13], [19, 40]]);
|
|
775
792
|
}));
|
|
776
793
|
return function loadVectorsAsync(_x) {
|
|
777
794
|
return _ref6.apply(this, arguments);
|
|
@@ -781,24 +798,39 @@ var initVectors = exports.initVectors = function initVectors(store) {
|
|
|
781
798
|
vectorStore = store;
|
|
782
799
|
};
|
|
783
800
|
var loadDefaultVocabulary = exports.loadDefaultVocabulary = function loadDefaultVocabulary() {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
801
|
+
if (isInitialized) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
(0, _ufo.startExp)(_ufo.EXPERIENCE_NAME.LOAD_VOCABULARY, 'singleton');
|
|
805
|
+
try {
|
|
806
|
+
// 1. Load the Atlassian Domain (L2)
|
|
807
|
+
var data = _vocabulary_10k.default;
|
|
808
|
+
var terms = Object.entries(data.words).map(function (_ref7) {
|
|
809
|
+
var _ref8 = (0, _slicedToArray2.default)(_ref7, 2),
|
|
810
|
+
word = _ref8[0],
|
|
811
|
+
stats = _ref8[1];
|
|
812
|
+
return {
|
|
813
|
+
word: word,
|
|
814
|
+
freq: stats.freq,
|
|
815
|
+
docFreq: stats.doc_freq,
|
|
816
|
+
authorFreq: stats.author_freq
|
|
817
|
+
};
|
|
818
|
+
});
|
|
819
|
+
initVocabulary({
|
|
820
|
+
terms: terms
|
|
821
|
+
});
|
|
800
822
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
823
|
+
// 2. Load General English (L3)
|
|
824
|
+
var l3Words = _l3_vocabulary.default;
|
|
825
|
+
initL3Vocabulary(l3Words);
|
|
826
|
+
(0, _ufo.succeedExp)(_ufo.EXPERIENCE_NAME.LOAD_VOCABULARY, 'singleton', {
|
|
827
|
+
l2WordCount: terms.length,
|
|
828
|
+
l3WordCount: l3Words.length
|
|
829
|
+
});
|
|
830
|
+
} catch (e) {
|
|
831
|
+
(0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.LOAD_VOCABULARY, 'singleton', {
|
|
832
|
+
errorType: 'parse_error'
|
|
833
|
+
});
|
|
834
|
+
throw e;
|
|
835
|
+
}
|
|
804
836
|
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UFO experience tracking helpers for `@atlaskit/editor-plugin-autocomplete`.
|
|
3
|
+
*
|
|
4
|
+
* Pattern follows `packages/linking-platform/smart-card/src/state/analytics/ufoExperiences.ts`
|
|
5
|
+
* (ConcurrentExperience keyed per request/instance) plus a try/catch safety wrap
|
|
6
|
+
* inspired by `packages/editor/collab-provider/src/analytics/ufo.ts` so a UFO
|
|
7
|
+
* runtime error can never break the autocomplete feature.
|
|
8
|
+
*
|
|
9
|
+
* Experience names must be registered in DataPortal's `task` attribute before
|
|
10
|
+
* they can power FE Reliability SLOs:
|
|
11
|
+
* https://hello.atlassian.net/wiki/spaces/AA6/pages/3961393753
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { ConcurrentExperience, ExperiencePerformanceTypes, ExperienceTypes } from '@atlaskit/ufo';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Experience name strings are surfaced downstream by the UFO pipeline as
|
|
18
|
+
* `platform.fe.<type>.<platform.component>.<name>` (e.g.
|
|
19
|
+
* `platform.fe.operation.editor-plugin-autocomplete.slow-lane-fetch`), so
|
|
20
|
+
* names here should be short, dot-free, and must not repeat the component
|
|
21
|
+
* name. This matches the convention used by `@atlaskit/emoji`'s
|
|
22
|
+
* `ufoExperiences` map (e.g. `'emoji-rendered'`).
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Only experiences with meaningful async work and a real success/failure
|
|
26
|
+
* outcome belong on UFO (which measures latency and success rate for FE
|
|
27
|
+
* Reliability SLOs). Per-keystroke suggestion lifecycle counters (viewed /
|
|
28
|
+
* inserted / dismissed) are tracked via analytics-next instead — they would
|
|
29
|
+
* otherwise emit zero-duration events on every word boundary.
|
|
30
|
+
*/
|
|
31
|
+
export const EXPERIENCE_NAME = {
|
|
32
|
+
SLOW_LANE_FETCH: 'slow-lane-fetch',
|
|
33
|
+
LOAD_VOCABULARY: 'load-vocabulary',
|
|
34
|
+
LOAD_VECTORS: 'load-vectors'
|
|
35
|
+
};
|
|
36
|
+
const isUfoEnabled = () => {
|
|
37
|
+
var _process, _process$env;
|
|
38
|
+
return !((_process = process) !== null && _process !== void 0 && (_process$env = _process.env) !== null && _process$env !== void 0 && _process$env.REACT_SSR);
|
|
39
|
+
};
|
|
40
|
+
const operationConfig = {
|
|
41
|
+
platform: {
|
|
42
|
+
component: 'editor-plugin-autocomplete'
|
|
43
|
+
},
|
|
44
|
+
type: ExperienceTypes.Operation,
|
|
45
|
+
performanceType: ExperiencePerformanceTypes.Custom,
|
|
46
|
+
performanceConfig: {
|
|
47
|
+
histogram: {
|
|
48
|
+
[ExperiencePerformanceTypes.Custom]: {
|
|
49
|
+
duration: '50_100_250_500_1000_2000_4000'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const experiences = {
|
|
55
|
+
[EXPERIENCE_NAME.SLOW_LANE_FETCH]: new ConcurrentExperience(EXPERIENCE_NAME.SLOW_LANE_FETCH, operationConfig),
|
|
56
|
+
[EXPERIENCE_NAME.LOAD_VOCABULARY]: new ConcurrentExperience(EXPERIENCE_NAME.LOAD_VOCABULARY, operationConfig),
|
|
57
|
+
[EXPERIENCE_NAME.LOAD_VECTORS]: new ConcurrentExperience(EXPERIENCE_NAME.LOAD_VECTORS, operationConfig)
|
|
58
|
+
};
|
|
59
|
+
export const startExp = (name, id, metadata) => {
|
|
60
|
+
if (!isUfoEnabled()) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const instance = experiences[name].getInstance(id);
|
|
65
|
+
instance.start();
|
|
66
|
+
if (metadata) {
|
|
67
|
+
instance.addMetadata(metadata);
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
// UFO errors must never break the plugin
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
export const succeedExp = (name, id, metadata) => {
|
|
74
|
+
if (!isUfoEnabled()) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
experiences[name].getInstance(id).success({
|
|
79
|
+
metadata
|
|
80
|
+
});
|
|
81
|
+
} catch {
|
|
82
|
+
// UFO errors must never break the plugin
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
export const failExp = (name, id, metadata) => {
|
|
86
|
+
if (!isUfoEnabled()) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
experiences[name].getInstance(id).failure({
|
|
91
|
+
metadata
|
|
92
|
+
});
|
|
93
|
+
} catch {
|
|
94
|
+
// UFO errors must never break the plugin
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
export const abortExp = (name, id, reason) => {
|
|
98
|
+
if (!isUfoEnabled()) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
experiences[name].getInstance(id).abort(reason ? {
|
|
103
|
+
metadata: {
|
|
104
|
+
reason
|
|
105
|
+
}
|
|
106
|
+
} : undefined);
|
|
107
|
+
} catch {
|
|
108
|
+
// UFO errors must never break the plugin
|
|
109
|
+
}
|
|
110
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/* eslint-disable @atlaskit/editor/no-re-export */
|
|
2
|
+
export { getLastPredictionDebug, getPredictorStatus, incrementSessionFreq, ingestDocumentPage, initL3Vocabulary, initVectors, initVocabulary, loadDefaultVocabulary, loadVectorsAsync, predict } from '../pm-plugins/text-predictor';
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
2
3
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
3
4
|
import { keydownHandler } from '@atlaskit/editor-prosemirror/keymap';
|
|
4
5
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
5
6
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
6
7
|
import { createGhostTextDecorationSet } from './ghost-text-decoration';
|
|
8
|
+
import { createLocalSlowLaneClient } from './local-slow-lane-client';
|
|
7
9
|
import { createSlowLaneClient, setDefaultSlowLaneClient, isWordBoundary } from './slow-lane-client';
|
|
8
10
|
import { predict, loadDefaultVocabulary, loadVectorsAsync, incrementSessionFreq, ingestDocumentPage } from './text-predictor';
|
|
9
|
-
const SLOW_LANE_ENDPOINT = '/gateway/api/v1/autocomplete/typeahead-encodings';
|
|
10
11
|
export const autocompletePluginKey = new PluginKey('autocomplete');
|
|
11
12
|
const DEBOUNCE_MS = 150;
|
|
13
|
+
const hasDestroy = client => 'destroy' in client && typeof client.destroy === 'function';
|
|
12
14
|
const createInitialState = () => ({
|
|
13
15
|
ghostText: '',
|
|
14
16
|
ghostPosition: -1,
|
|
@@ -61,17 +63,23 @@ const setAutocompleteMeta = (tr, meta) => {
|
|
|
61
63
|
*/
|
|
62
64
|
let lastShownGhostText = '';
|
|
63
65
|
const showGhostText = (view, text, position) => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
try {
|
|
67
|
+
const {
|
|
68
|
+
state,
|
|
69
|
+
dispatch
|
|
70
|
+
} = view;
|
|
71
|
+
const decorationSet = createGhostTextDecorationSet(state, position, text);
|
|
72
|
+
const tr = setAutocompleteMeta(state.tr, {
|
|
73
|
+
ghostText: text,
|
|
74
|
+
ghostPosition: position,
|
|
75
|
+
decorationSet
|
|
76
|
+
});
|
|
77
|
+
dispatch(tr);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
logException(error, {
|
|
80
|
+
location: 'editor-plugin-autocomplete/showGhostText'
|
|
81
|
+
});
|
|
82
|
+
}
|
|
75
83
|
};
|
|
76
84
|
|
|
77
85
|
/**
|
|
@@ -83,12 +91,19 @@ const clearGhostText = (state, dispatch) => {
|
|
|
83
91
|
return false;
|
|
84
92
|
}
|
|
85
93
|
if (dispatch) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
try {
|
|
95
|
+
const tr = setAutocompleteMeta(state.tr, {
|
|
96
|
+
ghostText: '',
|
|
97
|
+
ghostPosition: -1,
|
|
98
|
+
decorationSet: DecorationSet.empty
|
|
99
|
+
});
|
|
100
|
+
dispatch(tr);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
logException(error, {
|
|
103
|
+
location: 'editor-plugin-autocomplete/clearGhostText'
|
|
104
|
+
});
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
92
107
|
}
|
|
93
108
|
return true;
|
|
94
109
|
};
|
|
@@ -102,17 +117,24 @@ const acceptGhostText = (state, dispatch) => {
|
|
|
102
117
|
return false;
|
|
103
118
|
}
|
|
104
119
|
if (dispatch) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
120
|
+
try {
|
|
121
|
+
const {
|
|
122
|
+
ghostText,
|
|
123
|
+
ghostPosition
|
|
124
|
+
} = pluginState;
|
|
125
|
+
let tr = state.tr.insertText(ghostText, ghostPosition);
|
|
126
|
+
tr = setAutocompleteMeta(tr, {
|
|
127
|
+
ghostText: '',
|
|
128
|
+
ghostPosition: -1,
|
|
129
|
+
decorationSet: DecorationSet.empty
|
|
130
|
+
});
|
|
131
|
+
dispatch(tr);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
logException(error, {
|
|
134
|
+
location: 'editor-plugin-autocomplete/acceptGhostText'
|
|
135
|
+
});
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
116
138
|
}
|
|
117
139
|
return true;
|
|
118
140
|
};
|
|
@@ -171,12 +193,23 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
171
193
|
let dismissedContext = null;
|
|
172
194
|
let lastSuggestionTypedLength = 0;
|
|
173
195
|
let lastSuggestionLength = 0;
|
|
174
|
-
const
|
|
196
|
+
const fireSuggestionDismissedAnalytics = reason => {
|
|
175
197
|
var _api$analytics;
|
|
198
|
+
api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.fireAnalyticsEvent({
|
|
199
|
+
action: ACTION.SUGGESTION_DISMISSED,
|
|
200
|
+
actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
|
|
201
|
+
eventType: EVENT_TYPE.TRACK,
|
|
202
|
+
attributes: {
|
|
203
|
+
reason
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
const fireSuggestionInsertedAnalytics = ghostText => {
|
|
208
|
+
var _api$analytics2;
|
|
176
209
|
const typedLength = lastSuggestionTypedLength;
|
|
177
210
|
const suggestionLength = lastSuggestionLength || typedLength + ghostText.length;
|
|
178
211
|
const kssDelta = suggestionLength - typedLength;
|
|
179
|
-
api === null || api === void 0 ? void 0 : (_api$
|
|
212
|
+
api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions.fireAnalyticsEvent({
|
|
180
213
|
action: ACTION.SUGGESTION_INSERTED,
|
|
181
214
|
actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
|
|
182
215
|
eventType: EVENT_TYPE.TRACK,
|
|
@@ -187,9 +220,11 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
187
220
|
}
|
|
188
221
|
});
|
|
189
222
|
};
|
|
190
|
-
const slowLaneClient =
|
|
223
|
+
const slowLaneClient = options !== null && options !== void 0 && options.useLocalModel ? createLocalSlowLaneClient({
|
|
224
|
+
debounceMs: 300
|
|
225
|
+
}) : createSlowLaneClient({
|
|
191
226
|
baseUrl: '',
|
|
192
|
-
|
|
227
|
+
debounceMs: 300
|
|
193
228
|
});
|
|
194
229
|
setDefaultSlowLaneClient(slowLaneClient);
|
|
195
230
|
|
|
@@ -203,47 +238,53 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
203
238
|
clearTimeout(debounceTimer);
|
|
204
239
|
}
|
|
205
240
|
debounceTimer = setTimeout(() => {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
241
|
+
try {
|
|
242
|
+
const {
|
|
243
|
+
state
|
|
244
|
+
} = view;
|
|
245
|
+
const {
|
|
246
|
+
selection
|
|
247
|
+
} = state;
|
|
212
248
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
249
|
+
// Only predict for cursor selections (not range selections)
|
|
250
|
+
if (!selection.empty) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const textBefore = getTextBeforeCursor(state);
|
|
218
254
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
255
|
+
// Suppress re-showing the same suggestion the user just dismissed.
|
|
256
|
+
// Once the text context changes (user types or deletes), this clears automatically.
|
|
257
|
+
if (textBefore === dismissedContext) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
dismissedContext = null;
|
|
225
261
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
262
|
+
// Don't predict if there's not enough context
|
|
263
|
+
if (textBefore.trim().length < 3) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
230
266
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
267
|
+
// Tier 1 prediction is synchronous -- no async needed
|
|
268
|
+
const prediction = predict(textBefore);
|
|
269
|
+
if (prediction && prediction.length > 0) {
|
|
270
|
+
const typedLength = getTypedLengthForPrediction(textBefore);
|
|
271
|
+
lastSuggestionTypedLength = typedLength;
|
|
272
|
+
lastSuggestionLength = typedLength + prediction.length;
|
|
273
|
+
showGhostText(view, prediction, selection.from);
|
|
274
|
+
if (prediction !== lastShownGhostText) {
|
|
275
|
+
var _api$analytics3;
|
|
276
|
+
lastShownGhostText = prediction;
|
|
277
|
+
api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent({
|
|
278
|
+
action: ACTION.SUGGESTION_VIEWED,
|
|
279
|
+
actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
|
|
280
|
+
eventType: EVENT_TYPE.TRACK
|
|
281
|
+
});
|
|
282
|
+
}
|
|
246
283
|
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
logException(error, {
|
|
286
|
+
location: 'editor-plugin-autocomplete/schedulePrediction'
|
|
287
|
+
});
|
|
247
288
|
}
|
|
248
289
|
}, DEBOUNCE_MS);
|
|
249
290
|
};
|
|
@@ -344,6 +385,7 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
344
385
|
const didClear = clearGhostText(state, dispatch);
|
|
345
386
|
if (didClear) {
|
|
346
387
|
dismissedContext = getTextBeforeCursor(state);
|
|
388
|
+
fireSuggestionDismissedAnalytics('escape');
|
|
347
389
|
}
|
|
348
390
|
return didClear;
|
|
349
391
|
}
|
|
@@ -353,14 +395,25 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
353
395
|
const pluginState = autocompletePluginKey.getState(view.state);
|
|
354
396
|
if (pluginState !== null && pluginState !== void 0 && pluginState.ghostText) {
|
|
355
397
|
clearGhostText(view.state, view.dispatch);
|
|
398
|
+
fireSuggestionDismissedAnalytics('blur');
|
|
356
399
|
}
|
|
357
400
|
return false;
|
|
358
401
|
},
|
|
359
402
|
focus: () => {
|
|
360
|
-
|
|
403
|
+
try {
|
|
404
|
+
loadDefaultVocabulary();
|
|
405
|
+
} catch (error) {
|
|
406
|
+
logException(error, {
|
|
407
|
+
location: 'editor-plugin-autocomplete/loadDefaultVocabulary'
|
|
408
|
+
});
|
|
409
|
+
}
|
|
361
410
|
loadVectorsAsync({
|
|
362
411
|
getBinaryUrl: options === null || options === void 0 ? void 0 : options.getVectorsBinaryUrl
|
|
363
|
-
}).catch(
|
|
412
|
+
}).catch(error => {
|
|
413
|
+
logException(error, {
|
|
414
|
+
location: 'editor-plugin-autocomplete/loadVectorsAsync'
|
|
415
|
+
});
|
|
416
|
+
});
|
|
364
417
|
if (!hasIngestedPage) {
|
|
365
418
|
hasIngestedPage = true;
|
|
366
419
|
if (options !== null && options !== void 0 && options.getContext) {
|
|
@@ -377,7 +430,11 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
377
430
|
ingestDocumentPage(context.parentCommentContent);
|
|
378
431
|
}
|
|
379
432
|
(_context$siblingComme4 = context.siblingCommentsContents) === null || _context$siblingComme4 === void 0 ? void 0 : _context$siblingComme4.forEach(ingestDocumentPage);
|
|
380
|
-
}).catch(
|
|
433
|
+
}).catch(error => {
|
|
434
|
+
logException(error, {
|
|
435
|
+
location: 'editor-plugin-autocomplete/getContext'
|
|
436
|
+
});
|
|
437
|
+
});
|
|
381
438
|
}
|
|
382
439
|
}
|
|
383
440
|
return false;
|
|
@@ -413,6 +470,9 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
413
470
|
if (debounceTimer) {
|
|
414
471
|
clearTimeout(debounceTimer);
|
|
415
472
|
}
|
|
473
|
+
if (hasDestroy(slowLaneClient)) {
|
|
474
|
+
slowLaneClient.destroy();
|
|
475
|
+
}
|
|
416
476
|
setDefaultSlowLaneClient(null);
|
|
417
477
|
}
|
|
418
478
|
})
|