@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.
- package/CHANGELOG.md +22 -0
- package/afm-cc/tsconfig.json +3 -1
- package/afm-products/tsconfig.json +3 -1
- package/build/tsconfig.json +0 -1
- package/dist/cjs/autocompletePlugin.js +3 -2
- package/dist/cjs/pm-plugins/autocomplete-plugin.js +59 -3
- package/dist/cjs/pm-plugins/debug-mode.js +24 -0
- package/dist/cjs/pm-plugins/slow-lane-client.js +42 -35
- package/dist/cjs/pm-plugins/text-predictor.js +12 -32
- package/dist/es2019/autocompletePlugin.js +3 -2
- package/dist/es2019/pm-plugins/autocomplete-plugin.js +59 -3
- package/dist/es2019/pm-plugins/debug-mode.js +18 -0
- package/dist/es2019/pm-plugins/slow-lane-client.js +29 -23
- package/dist/es2019/pm-plugins/text-predictor.js +6 -26
- package/dist/esm/autocompletePlugin.js +3 -2
- package/dist/esm/pm-plugins/autocomplete-plugin.js +59 -3
- package/dist/esm/pm-plugins/debug-mode.js +18 -0
- package/dist/esm/pm-plugins/slow-lane-client.js +43 -35
- package/dist/esm/pm-plugins/text-predictor.js +11 -31
- package/dist/types/autocompletePluginType.d.ts +3 -1
- package/dist/types/pm-plugins/autocomplete-plugin.d.ts +4 -2
- package/dist/types/pm-plugins/debug-mode.d.ts +12 -0
- package/dist/types/pm-plugins/text-predictor.d.ts +0 -2
- package/dist/types-ts4.5/autocompletePluginType.d.ts +5 -1
- package/dist/types-ts4.5/pm-plugins/autocomplete-plugin.d.ts +4 -2
- package/dist/types-ts4.5/pm-plugins/debug-mode.d.ts +12 -0
- package/dist/types-ts4.5/pm-plugins/text-predictor.d.ts +0 -2
- package/package.json +3 -2
- package/src/autocompletePlugin.tsx +2 -2
- package/src/autocompletePluginType.ts +3 -1
- package/src/pm-plugins/autocomplete-plugin.ts +70 -4
- package/src/pm-plugins/debug-mode.ts +18 -0
- package/src/pm-plugins/slow-lane-client.ts +53 -45
- package/src/pm-plugins/text-predictor.ts +12 -34
- package/tsconfig.app.json +6 -1
- package/tsconfig.dev.json +2 -1
- package/afm-jira/tsconfig.json +0 -30
|
@@ -33,8 +33,6 @@ interface VectorStore {
|
|
|
33
33
|
* expects a simple array of strings: ["about", "above", "actually", ...]
|
|
34
34
|
*/
|
|
35
35
|
export declare const initL3Vocabulary: (l3Words: string[]) => void;
|
|
36
|
-
/** Enable or disable debug logging. Also checks localStorage key `autocomplete-debug`. */
|
|
37
|
-
export declare const setDebugMode: (enabled: boolean) => void;
|
|
38
36
|
/**
|
|
39
37
|
* Get predictor status for debugging.
|
|
40
38
|
* vectorsLoaded: true when semantic scoring is active
|
|
@@ -1,6 +1,10 @@
|
|
|
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: [
|
|
8
|
+
OptionalPlugin<AnalyticsPlugin>
|
|
9
|
+
];
|
|
6
10
|
}>;
|
|
@@ -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
|
-
|
|
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;
|
|
@@ -33,8 +33,6 @@ interface VectorStore {
|
|
|
33
33
|
* expects a simple array of strings: ["about", "above", "actually", ...]
|
|
34
34
|
*/
|
|
35
35
|
export declare const initL3Vocabulary: (l3Words: string[]) => void;
|
|
36
|
-
/** Enable or disable debug logging. Also checks localStorage key `autocomplete-debug`. */
|
|
37
|
-
export declare const setDebugMode: (enabled: boolean) => void;
|
|
38
36
|
/**
|
|
39
37
|
* Get predictor status for debugging.
|
|
40
38
|
* vectorsLoaded: true when semantic scoring is active
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-autocomplete",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Client-side text autocomplete plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"wink-nlp": "^2.4.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@atlaskit/editor-common": "^114.
|
|
36
|
+
"@atlaskit/editor-common": "^114.22.0",
|
|
37
|
+
"@atlaskit/editor-plugin-analytics": "^10.0.0",
|
|
37
38
|
"react": "^18.2.0"
|
|
38
39
|
},
|
|
39
40
|
"techstack": {
|
|
@@ -2,7 +2,7 @@ import type { AutocompletePlugin } from './autocompletePluginType';
|
|
|
2
2
|
import { autocompletePluginKey, createAutocompletePlugin } from './pm-plugins/autocomplete-plugin';
|
|
3
3
|
import type { AutocompletePluginState } from './pm-plugins/autocomplete-plugin';
|
|
4
4
|
|
|
5
|
-
export const autocompletePlugin: AutocompletePlugin = ({ config: options }) => {
|
|
5
|
+
export const autocompletePlugin: AutocompletePlugin = ({ config: options, api }) => {
|
|
6
6
|
return {
|
|
7
7
|
name: 'autocomplete',
|
|
8
8
|
getSharedState(editorState): AutocompletePluginState | undefined {
|
|
@@ -16,7 +16,7 @@ export const autocompletePlugin: AutocompletePlugin = ({ config: options }) => {
|
|
|
16
16
|
return [
|
|
17
17
|
{
|
|
18
18
|
name: 'autocomplete',
|
|
19
|
-
plugin: () => createAutocompletePlugin(options),
|
|
19
|
+
plugin: () => createAutocompletePlugin(options, api),
|
|
20
20
|
},
|
|
21
21
|
];
|
|
22
22
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
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
|
|
|
3
4
|
import type {
|
|
4
5
|
AutocompletePluginOptions,
|
|
@@ -10,5 +11,6 @@ export type AutocompletePlugin = NextEditorPlugin<
|
|
|
10
11
|
{
|
|
11
12
|
pluginConfiguration?: AutocompletePluginOptions | undefined;
|
|
12
13
|
sharedState: AutocompletePluginState | undefined;
|
|
14
|
+
dependencies: [OptionalPlugin<AnalyticsPlugin>];
|
|
13
15
|
}
|
|
14
16
|
>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
1
2
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
3
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
2
4
|
import { keydownHandler } from '@atlaskit/editor-prosemirror/keymap';
|
|
3
5
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
4
6
|
import type {
|
|
@@ -9,6 +11,8 @@ import type {
|
|
|
9
11
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
10
12
|
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
11
13
|
|
|
14
|
+
import type { AutocompletePlugin } from '../autocompletePluginType';
|
|
15
|
+
|
|
12
16
|
import { createGhostTextDecorationSet } from './ghost-text-decoration';
|
|
13
17
|
import { createSlowLaneClient, setDefaultSlowLaneClient, isWordBoundary } from './slow-lane-client';
|
|
14
18
|
import {
|
|
@@ -21,7 +25,7 @@ import {
|
|
|
21
25
|
|
|
22
26
|
const SLOW_LANE_ENDPOINT = '/gateway/api/v1/autocomplete/typeahead-encodings';
|
|
23
27
|
|
|
24
|
-
export const autocompletePluginKey = new PluginKey('autocomplete');
|
|
28
|
+
export const autocompletePluginKey: PluginKey = new PluginKey('autocomplete');
|
|
25
29
|
|
|
26
30
|
const DEBOUNCE_MS = 150;
|
|
27
31
|
|
|
@@ -90,6 +94,8 @@ const setAutocompleteMeta = (
|
|
|
90
94
|
/**
|
|
91
95
|
* Apply a ghost text suggestion to the editor state.
|
|
92
96
|
*/
|
|
97
|
+
let lastShownGhostText = '';
|
|
98
|
+
|
|
93
99
|
const showGhostText = (view: EditorView, text: string, position: number): void => {
|
|
94
100
|
const { state, dispatch } = view;
|
|
95
101
|
const decorationSet = createGhostTextDecorationSet(state, position, text);
|
|
@@ -196,7 +202,20 @@ const buildSlowLaneText = (docText: string, context?: AutocompleteContext): stri
|
|
|
196
202
|
return lines.join('\n');
|
|
197
203
|
};
|
|
198
204
|
|
|
199
|
-
|
|
205
|
+
const getTypedLengthForPrediction = (textBefore: string): number => {
|
|
206
|
+
if (isWordBoundary(textBefore)) {
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
211
|
+
const trailingToken = textBefore.match(/([^\s.,;:!?]*)$/)?.[1] ?? '';
|
|
212
|
+
return trailingToken.length;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export const createAutocompletePlugin = (
|
|
216
|
+
options?: AutocompletePluginOptions,
|
|
217
|
+
api?: ExtractInjectionAPI<AutocompletePlugin>,
|
|
218
|
+
): SafePlugin<AutocompletePluginState> => {
|
|
200
219
|
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
201
220
|
let hasIngestedPage = false;
|
|
202
221
|
let resolvedContext: AutocompleteContext | undefined;
|
|
@@ -213,6 +232,25 @@ export const createAutocompletePlugin = (options?: AutocompletePluginOptions) =>
|
|
|
213
232
|
* re-showing the same suggestion. Resets to null as soon as the text changes.
|
|
214
233
|
*/
|
|
215
234
|
let dismissedContext: string | null = null;
|
|
235
|
+
let lastSuggestionTypedLength = 0;
|
|
236
|
+
let lastSuggestionLength = 0;
|
|
237
|
+
|
|
238
|
+
const fireSuggestionInsertedAnalytics = (ghostText: string): void => {
|
|
239
|
+
const typedLength = lastSuggestionTypedLength;
|
|
240
|
+
const suggestionLength = lastSuggestionLength || typedLength + ghostText.length;
|
|
241
|
+
const kssDelta = suggestionLength - typedLength;
|
|
242
|
+
|
|
243
|
+
api?.analytics?.actions.fireAnalyticsEvent({
|
|
244
|
+
action: ACTION.SUGGESTION_INSERTED,
|
|
245
|
+
actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
|
|
246
|
+
eventType: EVENT_TYPE.TRACK,
|
|
247
|
+
attributes: {
|
|
248
|
+
suggestionLength,
|
|
249
|
+
typedLength,
|
|
250
|
+
kssDelta,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
};
|
|
216
254
|
|
|
217
255
|
const slowLaneClient = createSlowLaneClient({
|
|
218
256
|
baseUrl: '',
|
|
@@ -257,7 +295,19 @@ export const createAutocompletePlugin = (options?: AutocompletePluginOptions) =>
|
|
|
257
295
|
const prediction = predict(textBefore);
|
|
258
296
|
|
|
259
297
|
if (prediction && prediction.length > 0) {
|
|
298
|
+
const typedLength = getTypedLengthForPrediction(textBefore);
|
|
299
|
+
lastSuggestionTypedLength = typedLength;
|
|
300
|
+
lastSuggestionLength = typedLength + prediction.length;
|
|
301
|
+
|
|
260
302
|
showGhostText(view, prediction, selection.from);
|
|
303
|
+
if (prediction !== lastShownGhostText) {
|
|
304
|
+
lastShownGhostText = prediction;
|
|
305
|
+
api?.analytics?.actions.fireAnalyticsEvent({
|
|
306
|
+
action: ACTION.SUGGESTION_VIEWED,
|
|
307
|
+
actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
|
|
308
|
+
eventType: EVENT_TYPE.TRACK,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
261
311
|
}
|
|
262
312
|
}, DEBOUNCE_MS);
|
|
263
313
|
};
|
|
@@ -345,13 +395,29 @@ export const createAutocompletePlugin = (options?: AutocompletePluginOptions) =>
|
|
|
345
395
|
|
|
346
396
|
handleKeyDown: keydownHandler({
|
|
347
397
|
Tab: (state: EditorState, dispatch?: (tr: Transaction) => void) => {
|
|
398
|
+
const pluginState = autocompletePluginKey.getState(state) as
|
|
399
|
+
| AutocompletePluginState
|
|
400
|
+
| undefined;
|
|
401
|
+
const ghostText = pluginState?.ghostText ?? '';
|
|
348
402
|
const accepted = acceptGhostText(state, dispatch);
|
|
349
|
-
if (accepted)
|
|
403
|
+
if (accepted) {
|
|
404
|
+
justAccepted = true;
|
|
405
|
+
lastShownGhostText = '';
|
|
406
|
+
fireSuggestionInsertedAnalytics(ghostText);
|
|
407
|
+
}
|
|
350
408
|
return accepted;
|
|
351
409
|
},
|
|
352
410
|
ArrowRight: (state: EditorState, dispatch?: (tr: Transaction) => void) => {
|
|
411
|
+
const pluginState = autocompletePluginKey.getState(state) as
|
|
412
|
+
| AutocompletePluginState
|
|
413
|
+
| undefined;
|
|
414
|
+
const ghostText = pluginState?.ghostText ?? '';
|
|
353
415
|
const accepted = acceptGhostText(state, dispatch);
|
|
354
|
-
if (accepted)
|
|
416
|
+
if (accepted) {
|
|
417
|
+
justAccepted = true;
|
|
418
|
+
lastShownGhostText = '';
|
|
419
|
+
fireSuggestionInsertedAnalytics(ghostText);
|
|
420
|
+
}
|
|
355
421
|
return accepted;
|
|
356
422
|
},
|
|
357
423
|
Escape: (state: EditorState, dispatch?: (tr: Transaction) => void) => {
|
|
@@ -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 const isAutocompleteDebugEnabled = (): boolean => {
|
|
13
|
+
try {
|
|
14
|
+
return typeof localStorage !== 'undefined' && localStorage.getItem('atl-ctc-dbg') === '1';
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -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. */
|
|
@@ -95,19 +97,20 @@ export const createSlowLaneClient = (
|
|
|
95
97
|
session_id: sessionId,
|
|
96
98
|
};
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
if (isAutocompleteDebugEnabled()) {
|
|
101
|
+
// eslint-disable-next-line no-console
|
|
102
|
+
console.groupCollapsed(
|
|
103
|
+
`%c[SlowLane] %c📤 Sending context | ${text.length} chars`,
|
|
104
|
+
'color: #9c27b0; font-weight: bold;',
|
|
105
|
+
'color: inherit;',
|
|
106
|
+
);
|
|
107
|
+
text.split('\n').forEach((line, i, arr) => {
|
|
108
|
+
// eslint-disable-next-line no-console
|
|
109
|
+
console.log(` ${i === arr.length - 1 ? '▶' : ' '} ${line}`);
|
|
110
|
+
});
|
|
106
111
|
// eslint-disable-next-line no-console
|
|
107
|
-
console.
|
|
108
|
-
}
|
|
109
|
-
// eslint-disable-next-line no-console
|
|
110
|
-
console.groupEnd();
|
|
112
|
+
console.groupEnd();
|
|
113
|
+
}
|
|
111
114
|
|
|
112
115
|
try {
|
|
113
116
|
const res = await fetchFn(url, {
|
|
@@ -119,12 +122,14 @@ export const createSlowLaneClient = (
|
|
|
119
122
|
if (!res.ok) {
|
|
120
123
|
storedContextVector = null;
|
|
121
124
|
storedLmLogits = null;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
if (isAutocompleteDebugEnabled()) {
|
|
126
|
+
// eslint-disable-next-line no-console
|
|
127
|
+
console.log(
|
|
128
|
+
`%c[SlowLane] %c❌ Request failed (${res.status})`,
|
|
129
|
+
'color: #9c27b0; font-weight: bold;',
|
|
130
|
+
'color: #f44336;',
|
|
131
|
+
);
|
|
132
|
+
}
|
|
128
133
|
return;
|
|
129
134
|
}
|
|
130
135
|
|
|
@@ -142,27 +147,28 @@ export const createSlowLaneClient = (
|
|
|
142
147
|
storedLmLogits = null;
|
|
143
148
|
}
|
|
144
149
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
150
|
+
if (isAutocompleteDebugEnabled()) {
|
|
151
|
+
// eslint-disable-next-line no-console
|
|
152
|
+
console.groupCollapsed(
|
|
153
|
+
`%c[SlowLane] %c📥 Response received`,
|
|
154
|
+
'color: #9c27b0; font-weight: bold;',
|
|
155
|
+
'color: inherit;',
|
|
156
|
+
);
|
|
157
|
+
// eslint-disable-next-line no-console
|
|
158
|
+
console.log(
|
|
159
|
+
storedContextVector
|
|
160
|
+
? `✅ semantic_vector: ${storedContextVector.length} dims`
|
|
161
|
+
: '❌ No semantic_vector',
|
|
162
|
+
);
|
|
163
|
+
// eslint-disable-next-line no-console
|
|
164
|
+
console.log(
|
|
165
|
+
storedLmLogits
|
|
166
|
+
? `✅ lm_logits: ${Object.keys(storedLmLogits).length} tokens`
|
|
167
|
+
: '❌ No lm_logits',
|
|
168
|
+
);
|
|
169
|
+
// eslint-disable-next-line no-console
|
|
170
|
+
console.groupEnd();
|
|
171
|
+
}
|
|
166
172
|
|
|
167
173
|
onUpdate?.({
|
|
168
174
|
textLength: text.length,
|
|
@@ -173,12 +179,14 @@ export const createSlowLaneClient = (
|
|
|
173
179
|
} catch (e) {
|
|
174
180
|
storedContextVector = null;
|
|
175
181
|
storedLmLogits = null;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
+
if (isAutocompleteDebugEnabled()) {
|
|
183
|
+
// eslint-disable-next-line no-console
|
|
184
|
+
console.log(
|
|
185
|
+
'%c[SlowLane] %c❌ Network error — context cleared',
|
|
186
|
+
'color: #9c27b0; font-weight: bold;',
|
|
187
|
+
'color: #f44336;',
|
|
188
|
+
);
|
|
189
|
+
}
|
|
182
190
|
}
|
|
183
191
|
};
|
|
184
192
|
|
|
@@ -20,6 +20,7 @@ import l3VocabularyData from './data/l3_vocabulary.json';
|
|
|
20
20
|
import vocabularyData from './data/vocabulary_10k.json';
|
|
21
21
|
import wordIndexData from './data/word_index_10k.json';
|
|
22
22
|
// import { rankCandidates, isGrammarAllowed } from './scoring-pipeline';
|
|
23
|
+
import { isAutocompleteDebugEnabled } from './debug-mode';
|
|
23
24
|
import { rankCandidates, STAGE1_WEIGHT, STAGE2_WEIGHT, MIN_STAGE1_SCORE } from './scoring-pipeline';
|
|
24
25
|
import type { ScoringCandidate } from './scoring-pipeline';
|
|
25
26
|
import { getStoredContextVector, getStoredLmLogits } from './slow-lane-client';
|
|
@@ -183,7 +184,7 @@ export const initL3Vocabulary = (l3Words: string[]): void => {
|
|
|
183
184
|
// loses to any domain word in Stage 1, but still scores above 0.
|
|
184
185
|
l3Trie.insert(word, L3_BASELINE_FREQ, 0, 0);
|
|
185
186
|
}
|
|
186
|
-
if (
|
|
187
|
+
if (isAutocompleteDebugEnabled()) {
|
|
187
188
|
// eslint-disable-next-line no-console
|
|
188
189
|
console.log(`[text-predictor] L3 General English loaded: ${l3Words.length} words`);
|
|
189
190
|
}
|
|
@@ -199,8 +200,6 @@ let vectorStore: VectorStore | null = null;
|
|
|
199
200
|
|
|
200
201
|
let vectorsLoadStarted = false;
|
|
201
202
|
|
|
202
|
-
let debugMode = true;
|
|
203
|
-
|
|
204
203
|
let lastPredictionDebug: {
|
|
205
204
|
contextWords: string[];
|
|
206
205
|
currentWord: string;
|
|
@@ -216,8 +215,6 @@ let lastPredictionDebug: {
|
|
|
216
215
|
}>;
|
|
217
216
|
} | null = null;
|
|
218
217
|
|
|
219
|
-
let hasLoggedSemanticActive = false;
|
|
220
|
-
|
|
221
218
|
/** Get vector for a word from the store. */
|
|
222
219
|
const getWordVector = (word: string): Float32Array | null => {
|
|
223
220
|
if (!vectorStore) {
|
|
@@ -306,18 +303,6 @@ const extractPreviousWord = (text: string): string => {
|
|
|
306
303
|
|
|
307
304
|
// ─── Debug Helpers ───────────────────────────────────────────────────────────
|
|
308
305
|
|
|
309
|
-
/** Enable or disable debug logging. Also checks localStorage key `autocomplete-debug`. */
|
|
310
|
-
export const setDebugMode = (enabled: boolean): void => {
|
|
311
|
-
debugMode = enabled;
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
/** Check localStorage for autocomplete-debug on first access. */
|
|
315
|
-
const ensureDebugModeFromStorage = (): void => {
|
|
316
|
-
if (typeof localStorage !== 'undefined' && localStorage.getItem('autocomplete-debug') === '1') {
|
|
317
|
-
debugMode = true;
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
|
|
321
306
|
/**
|
|
322
307
|
* Get predictor status for debugging.
|
|
323
308
|
* vectorsLoaded: true when semantic scoring is active
|
|
@@ -329,7 +314,6 @@ export const getPredictorStatus = (): {
|
|
|
329
314
|
vectorsLoadStarted: boolean;
|
|
330
315
|
wordCount: number;
|
|
331
316
|
} => {
|
|
332
|
-
ensureDebugModeFromStorage();
|
|
333
317
|
return {
|
|
334
318
|
vectorsLoaded: vectorStore !== null,
|
|
335
319
|
wordCount: vectorStore ? Object.keys(vectorStore.wordIndex).length : 0,
|
|
@@ -356,7 +340,6 @@ export const getLastPredictionDebug = (): {
|
|
|
356
340
|
word: string;
|
|
357
341
|
}>;
|
|
358
342
|
} | null => {
|
|
359
|
-
ensureDebugModeFromStorage();
|
|
360
343
|
return lastPredictionDebug;
|
|
361
344
|
};
|
|
362
345
|
|
|
@@ -401,7 +384,7 @@ export const ingestDocumentPage = (pageContent: string | undefined): void => {
|
|
|
401
384
|
}
|
|
402
385
|
}
|
|
403
386
|
|
|
404
|
-
if (
|
|
387
|
+
if (isAutocompleteDebugEnabled() && validBoostedWords.size > 0) {
|
|
405
388
|
// eslint-disable-next-line no-console
|
|
406
389
|
console.groupCollapsed(
|
|
407
390
|
`%c[L1 Session] %cPrimed ${validBoostedWords.size} valid dictionary words from page`,
|
|
@@ -416,8 +399,6 @@ export const ingestDocumentPage = (pageContent: string | undefined): void => {
|
|
|
416
399
|
};
|
|
417
400
|
|
|
418
401
|
export const predict = (textBefore: string): string | null => {
|
|
419
|
-
ensureDebugModeFromStorage();
|
|
420
|
-
|
|
421
402
|
if (!isInitialized) {
|
|
422
403
|
loadDefaultVocabulary();
|
|
423
404
|
}
|
|
@@ -506,7 +487,7 @@ export const predict = (textBefore: string): string | null => {
|
|
|
506
487
|
const lmLogits = getStoredLmLogits();
|
|
507
488
|
|
|
508
489
|
// Raw LM Output Logger
|
|
509
|
-
if (
|
|
490
|
+
if (isAutocompleteDebugEnabled() && lmLogits && Object.keys(lmLogits).length > 0) {
|
|
510
491
|
const rawLmTop = Object.entries(lmLogits)
|
|
511
492
|
.sort((a, b) => b[1] - a[1])
|
|
512
493
|
.slice(0, 5)
|
|
@@ -520,10 +501,6 @@ export const predict = (textBefore: string): string | null => {
|
|
|
520
501
|
|
|
521
502
|
const mode: 'cold' | 'warm' = contextVector ? 'warm' : 'cold';
|
|
522
503
|
|
|
523
|
-
if (debugMode && contextVector && !hasLoggedSemanticActive) {
|
|
524
|
-
hasLoggedSemanticActive = true;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
504
|
// Build ScoringCandidate array from TrieNodes
|
|
528
505
|
const scoringCandidates: ScoringCandidate[] = candidates.map(({ word, node }) => ({
|
|
529
506
|
word,
|
|
@@ -537,11 +514,13 @@ export const predict = (textBefore: string): string | null => {
|
|
|
537
514
|
// lmMax in rankCandidates reflects prefix-relevant signal, not the global distribution.
|
|
538
515
|
const prefix = currentWord.toLowerCase();
|
|
539
516
|
const prefixLmLogits = lmLogits
|
|
540
|
-
? Object.fromEntries(
|
|
541
|
-
Object.entries(lmLogits).filter(([word]) => word.startsWith(prefix))
|
|
542
|
-
)
|
|
517
|
+
? Object.fromEntries(Object.entries(lmLogits).filter(([word]) => word.startsWith(prefix)))
|
|
543
518
|
: null;
|
|
544
|
-
const {
|
|
519
|
+
const {
|
|
520
|
+
candidates: ranked,
|
|
521
|
+
grammarMeta,
|
|
522
|
+
pipelineDebug,
|
|
523
|
+
} = rankCandidates(
|
|
545
524
|
scoringCandidates,
|
|
546
525
|
contextVector,
|
|
547
526
|
(w: string) => getWordVector(w),
|
|
@@ -554,7 +533,7 @@ export const predict = (textBefore: string): string | null => {
|
|
|
554
533
|
const suggestion =
|
|
555
534
|
best && best.finalScore >= MIN_SCORE_THRESHOLD ? best.word.slice(currentWord.length) : null;
|
|
556
535
|
|
|
557
|
-
if (
|
|
536
|
+
if (isAutocompleteDebugEnabled()) {
|
|
558
537
|
const latencyMs = performance.now() - t0;
|
|
559
538
|
const tokens = tokenize(trimmed);
|
|
560
539
|
const contextWords = tokens.slice(-CONTEXT_WORDS);
|
|
@@ -770,8 +749,7 @@ export const loadVectorsAsync = async (options?: {
|
|
|
770
749
|
const dim = float32.length / nWords;
|
|
771
750
|
|
|
772
751
|
vectorStore = { float32, wordIndex, dim };
|
|
773
|
-
|
|
774
|
-
if (debugMode) {
|
|
752
|
+
if (isAutocompleteDebugEnabled()) {
|
|
775
753
|
// eslint-disable-next-line no-console
|
|
776
754
|
console.log('[text-predictor] Vectors loaded:', {
|
|
777
755
|
wordCount: nWords,
|
package/tsconfig.app.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"extends": "../../../tsconfig.base.json",
|
|
3
3
|
"include": ["./src/**/*.ts", "./src/**/*.tsx", "./src/**/*.json"],
|
|
4
4
|
"exclude": [
|
|
5
|
+
"**/*.docs.tsx",
|
|
5
6
|
"**/docs/**/*",
|
|
6
7
|
"**/__tests__/**/*",
|
|
7
8
|
"**/vr-tests/**/*",
|
|
@@ -29,12 +30,16 @@
|
|
|
29
30
|
"compilerOptions": {
|
|
30
31
|
"composite": true,
|
|
31
32
|
"outDir": "../../../tsDist/@atlaskit__editor-plugin-autocomplete/app",
|
|
32
|
-
"resolveJsonModule": true
|
|
33
|
+
"resolveJsonModule": true,
|
|
34
|
+
"isolatedDeclarations": true
|
|
33
35
|
},
|
|
34
36
|
"references": [
|
|
35
37
|
{
|
|
36
38
|
"path": "../editor-common/tsconfig.app.json"
|
|
37
39
|
},
|
|
40
|
+
{
|
|
41
|
+
"path": "../editor-plugin-analytics/tsconfig.app.json"
|
|
42
|
+
},
|
|
38
43
|
{
|
|
39
44
|
"path": "../editor-prosemirror/tsconfig.app.json"
|
|
40
45
|
}
|
package/tsconfig.dev.json
CHANGED
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"exclude": ["./dist/**/*", "./build/**/*", "./node_modules/**/*"],
|
|
29
29
|
"compilerOptions": {
|
|
30
30
|
"composite": true,
|
|
31
|
-
"outDir": "../../../tsDist/@atlaskit__editor-plugin-autocomplete/dev"
|
|
31
|
+
"outDir": "../../../tsDist/@atlaskit__editor-plugin-autocomplete/dev",
|
|
32
|
+
"isolatedDeclarations": true
|
|
32
33
|
},
|
|
33
34
|
"references": [
|
|
34
35
|
{
|
package/afm-jira/tsconfig.json
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../../tsconfig.local-consumption.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"target": "es5",
|
|
5
|
-
"outDir": "../../../../../jira/tsDist/@atlaskit__editor-plugin-autocomplete/app",
|
|
6
|
-
"rootDir": "../",
|
|
7
|
-
"composite": true,
|
|
8
|
-
"noCheck": true,
|
|
9
|
-
"resolveJsonModule": true
|
|
10
|
-
},
|
|
11
|
-
"include": [
|
|
12
|
-
"../src/**/*.ts",
|
|
13
|
-
"../src/**/*.tsx"
|
|
14
|
-
],
|
|
15
|
-
"exclude": [
|
|
16
|
-
"../src/**/__tests__/*",
|
|
17
|
-
"../src/**/*.test.*",
|
|
18
|
-
"../src/**/test.*",
|
|
19
|
-
"../src/**/mocks/*",
|
|
20
|
-
"../src/**/examples.*"
|
|
21
|
-
],
|
|
22
|
-
"references": [
|
|
23
|
-
{
|
|
24
|
-
"path": "../../editor-prosemirror/afm-jira/tsconfig.json"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"path": "../../editor-common/afm-jira/tsconfig.json"
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
}
|