@atlaskit/editor-plugin-limited-mode 14.0.0 → 14.1.1
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 +9 -0
- package/dist/cjs/limitedModePlugin.js +7 -4
- package/dist/cjs/pm-plugins/main.js +201 -18
- package/dist/cjs/pm-plugins/plugin-key.js +8 -0
- package/dist/cjs/pm-plugins/utils/latch-detector-types.js +5 -0
- package/dist/cjs/pm-plugins/utils/latch-detector.js +108 -0
- package/dist/cjs/pm-plugins/utils/latch-policy-types.js +1 -0
- package/dist/cjs/pm-plugins/utils/latch-policy.js +314 -0
- package/dist/es2019/limitedModePlugin.js +4 -3
- package/dist/es2019/pm-plugins/main.js +195 -16
- package/dist/es2019/pm-plugins/plugin-key.js +2 -0
- package/dist/es2019/pm-plugins/utils/latch-detector-types.js +1 -0
- package/dist/es2019/pm-plugins/utils/latch-detector.js +92 -0
- package/dist/es2019/pm-plugins/utils/latch-policy-types.js +0 -0
- package/dist/es2019/pm-plugins/utils/latch-policy.js +277 -0
- package/dist/esm/limitedModePlugin.js +6 -3
- package/dist/esm/pm-plugins/main.js +199 -16
- package/dist/esm/pm-plugins/plugin-key.js +2 -0
- package/dist/esm/pm-plugins/utils/latch-detector-types.js +1 -0
- package/dist/esm/pm-plugins/utils/latch-detector.js +102 -0
- package/dist/esm/pm-plugins/utils/latch-policy-types.js +0 -0
- package/dist/esm/pm-plugins/utils/latch-policy.js +307 -0
- package/dist/types/limitedModePluginType.d.ts +34 -1
- package/dist/types/pm-plugins/main.d.ts +12 -4
- package/dist/types/pm-plugins/plugin-key.d.ts +2 -0
- package/dist/types/pm-plugins/utils/latch-detector-types.d.ts +30 -0
- package/dist/types/pm-plugins/utils/latch-detector.d.ts +15 -0
- package/dist/types/pm-plugins/utils/latch-policy-types.d.ts +150 -0
- package/dist/types/pm-plugins/utils/latch-policy.d.ts +112 -0
- package/package.json +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-limited-mode
|
|
2
2
|
|
|
3
|
+
## 14.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
|
|
9
|
+
## 14.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`aec3fcfe13ff9`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/aec3fcfe13ff9) -
|
|
14
|
+
EDITOR-8303 Add runtime input-latency trigger for limited mode behind
|
|
15
|
+
platform_editor_dynamic_limited_mode. Limited mode can now latch mid-session when sustained slow
|
|
16
|
+
input or repeated browser freezes indicate the device is struggling, in addition to the existing
|
|
17
|
+
document-size decision. The latch is one-way. Nothing is constructed when the experiment is off.
|
|
18
|
+
|
|
19
|
+
How much evidence is needed is a single tunable, `requiredConfirmations`: that many qualifying
|
|
20
|
+
windows, each separated from the last by `confirmationGapMs`. The hardware no longer feeds into
|
|
21
|
+
the decision — `navigator.hardwareConcurrency` and `navigator.deviceMemory` are reported with the
|
|
22
|
+
`limitedModeLatched` event instead, so which devices latch can be answered from the data rather
|
|
23
|
+
than assumed up front.
|
|
24
|
+
|
|
25
|
+
`LimitedModePluginState` gains a derived `enabled` flag, which is now the single thing consumers
|
|
26
|
+
should branch on — the individual reasons (`documentSizeBreachesThreshold`, `latchPolicyBreached`)
|
|
27
|
+
no longer need to be combined at each call site, so a future reason needs no change outside this
|
|
28
|
+
plugin. `sharedState.enabled` reads from it.
|
|
29
|
+
|
|
30
|
+
Under the experiment the document decision is also evaluated on load and on document replacement
|
|
31
|
+
only, rather than on every transaction that changes the document. That check walks the whole
|
|
32
|
+
document, so it was a full-document scan per keystroke on exactly the pages least able to afford
|
|
33
|
+
one. The trade-off is that a document editing its way past the thresholds is not re-judged until
|
|
34
|
+
it next loads.
|
|
35
|
+
|
|
36
|
+
Consumers updated to react to a mid-session change: table sticky headers now subscribe instead of
|
|
37
|
+
reading once at construction, block-controls resets rather than freezes its state on entry, and
|
|
38
|
+
the expand, media and table nodeviews read the derived flag so they no longer miss a runtime
|
|
39
|
+
latch.
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- Updated dependencies
|
|
44
|
+
|
|
3
45
|
## 14.0.0
|
|
4
46
|
|
|
5
47
|
### Patch Changes
|
package/afm-cc/tsconfig.json
CHANGED
|
@@ -17,9 +17,18 @@
|
|
|
17
17
|
"../src/**/examples.*"
|
|
18
18
|
],
|
|
19
19
|
"references": [
|
|
20
|
+
{
|
|
21
|
+
"path": "../../editor-plugin-analytics/afm-cc/tsconfig.json"
|
|
22
|
+
},
|
|
20
23
|
{
|
|
21
24
|
"path": "../../editor-prosemirror/afm-cc/tsconfig.json"
|
|
22
25
|
},
|
|
26
|
+
{
|
|
27
|
+
"path": "../../../measurement/feature-gate-js-client/afm-cc/tsconfig.json"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"path": "../../../platform/feature-experiments/afm-cc/tsconfig.json"
|
|
31
|
+
},
|
|
23
32
|
{
|
|
24
33
|
"path": "../../../platform/feature-flags/afm-cc/tsconfig.json"
|
|
25
34
|
},
|
|
@@ -7,6 +7,7 @@ exports.limitedModePlugin = void 0;
|
|
|
7
7
|
var _nodeAnchor = require("@atlaskit/editor-common/node-anchor");
|
|
8
8
|
var _usePluginStateEffect = require("@atlaskit/editor-common/use-plugin-state-effect");
|
|
9
9
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
10
|
+
var _pluginKey = require("./pm-plugins/plugin-key");
|
|
10
11
|
var _main = require("./pm-plugins/main");
|
|
11
12
|
var limitedModePlugin = exports.limitedModePlugin = function limitedModePlugin(_ref) {
|
|
12
13
|
var api = _ref.api;
|
|
@@ -15,7 +16,9 @@ var limitedModePlugin = exports.limitedModePlugin = function limitedModePlugin(_
|
|
|
15
16
|
pmPlugins: function pmPlugins() {
|
|
16
17
|
return [{
|
|
17
18
|
name: 'limitedModePlugin',
|
|
18
|
-
plugin:
|
|
19
|
+
plugin: function plugin() {
|
|
20
|
+
return (0, _main.createPlugin)(api);
|
|
21
|
+
}
|
|
19
22
|
}];
|
|
20
23
|
},
|
|
21
24
|
getSharedState: function getSharedState(editorState) {
|
|
@@ -23,14 +26,14 @@ var limitedModePlugin = exports.limitedModePlugin = function limitedModePlugin(_
|
|
|
23
26
|
return {
|
|
24
27
|
get enabled() {
|
|
25
28
|
var _limitedModePluginKey, _limitedModePluginKey2;
|
|
26
|
-
return (_limitedModePluginKey = (_limitedModePluginKey2 =
|
|
29
|
+
return (_limitedModePluginKey = (_limitedModePluginKey2 = _pluginKey.limitedModePluginKey.getState(editorState)) === null || _limitedModePluginKey2 === void 0 ? void 0 : _limitedModePluginKey2.enabled) !== null && _limitedModePluginKey !== void 0 ? _limitedModePluginKey : false;
|
|
27
30
|
},
|
|
28
|
-
limitedModePluginKey:
|
|
31
|
+
limitedModePluginKey: _pluginKey.limitedModePluginKey
|
|
29
32
|
};
|
|
30
33
|
}
|
|
31
34
|
return {
|
|
32
35
|
enabled: false,
|
|
33
|
-
limitedModePluginKey:
|
|
36
|
+
limitedModePluginKey: _pluginKey.limitedModePluginKey
|
|
34
37
|
};
|
|
35
38
|
},
|
|
36
39
|
usePluginHook: function usePluginHook(_ref2) {
|
|
@@ -1,35 +1,218 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
3
4
|
Object.defineProperty(exports, "__esModule", {
|
|
4
5
|
value: true
|
|
5
6
|
});
|
|
6
|
-
exports.
|
|
7
|
+
exports.createPlugin = void 0;
|
|
8
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
7
10
|
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
|
|
8
11
|
var _shouldEnableLimitedMode = require("@atlaskit/editor-common/should-enable-limited-mode");
|
|
9
|
-
var
|
|
10
|
-
var
|
|
11
|
-
var
|
|
12
|
+
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
|
|
13
|
+
var _expVal = require("@atlaskit/platform-feature-experiments/exp-val");
|
|
14
|
+
var _pluginKey = require("./plugin-key");
|
|
15
|
+
var _latchDetector = require("./utils/latch-detector");
|
|
16
|
+
var _latchPolicy = require("./utils/latch-policy");
|
|
17
|
+
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; }
|
|
18
|
+
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) { (0, _defineProperty2.default)(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; }
|
|
19
|
+
/**
|
|
20
|
+
* Meta shape used to latch limited mode at runtime. Dispatching this is the entire delivery
|
|
21
|
+
* mechanism: the transaction flows through `SharedStateAPI.notifyListeners`, which diffs the plugin's
|
|
22
|
+
* shared state and notifies every consumer. No plugin re-registration and no schema rebuild.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Hardware hints, reported with a latch so it can be correlated with device class.
|
|
27
|
+
*
|
|
28
|
+
* Telemetry only — the policy no longer takes the hardware into account when deciding, so this
|
|
29
|
+
* exists to answer "which devices are latching" from the data rather than by assumption. Both hints
|
|
30
|
+
* are optional (`deviceMemory` is Chromium-only) and are simply absent where unsupported.
|
|
31
|
+
*/
|
|
32
|
+
var getDeviceHints = function getDeviceHints() {
|
|
33
|
+
if (typeof navigator === 'undefined') {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
var _ref = navigator,
|
|
37
|
+
hardwareConcurrency = _ref.hardwareConcurrency,
|
|
38
|
+
deviceMemory = _ref.deviceMemory;
|
|
39
|
+
return {
|
|
40
|
+
hardwareConcurrency: hardwareConcurrency,
|
|
41
|
+
deviceMemoryGb: deviceMemory
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** Control-arm state: the document decision alone, exactly as before the experiment. */
|
|
46
|
+
var documentOnlyState = function documentOnlyState(doc) {
|
|
47
|
+
var documentSizeBreachesThreshold = (0, _shouldEnableLimitedMode.shouldEnableLimitedModeForDocument)(doc);
|
|
48
|
+
return {
|
|
49
|
+
documentSizeBreachesThreshold: documentSizeBreachesThreshold,
|
|
50
|
+
latchPolicyBreached: false,
|
|
51
|
+
enabled: documentSizeBreachesThreshold
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Treatment-arm state: whatever the policy says, for both of its reasons. */
|
|
56
|
+
var policyState = function policyState(policy) {
|
|
57
|
+
return {
|
|
58
|
+
documentSizeBreachesThreshold: policy.isDocumentBreached(),
|
|
59
|
+
latchPolicyBreached: policy.isBreached(),
|
|
60
|
+
enabled: policy.isBreached()
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
var createPlugin = exports.createPlugin = function createPlugin(api, injectedPolicy) {
|
|
64
|
+
var detector;
|
|
65
|
+
var policy;
|
|
66
|
+
if (injectedPolicy) {
|
|
67
|
+
policy = injectedPolicy;
|
|
68
|
+
} else if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_dynamic_limited_mode')) {
|
|
69
|
+
var config = (0, _expVal.expVal)('platform_editor_dynamic_limited_mode', 'policyConfig', {
|
|
70
|
+
warmUpMs: 10000,
|
|
71
|
+
slowInputMs: 100,
|
|
72
|
+
latencyWindowSize: 12,
|
|
73
|
+
latencySlowSamplesRequired: 6,
|
|
74
|
+
freezeTaskMs: 600,
|
|
75
|
+
freezeTasksRequired: 3,
|
|
76
|
+
freezeWindowMs: 30000,
|
|
77
|
+
requiredConfirmations: 2,
|
|
78
|
+
confirmationGapMs: 30000,
|
|
79
|
+
bulkChangeNodeSize: 100,
|
|
80
|
+
bulkChangeSuppressionMs: 2000,
|
|
81
|
+
docSizeThreshold: 750000,
|
|
82
|
+
nodeCountThreshold: 5000
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Resolved once per editor rather than per transaction. When off, no policy is built and the
|
|
86
|
+
// document decision runs inline exactly as it did before this experiment.
|
|
87
|
+
policy = new _latchPolicy.LatchPolicy({
|
|
88
|
+
now: function now() {
|
|
89
|
+
return performance.now();
|
|
90
|
+
},
|
|
91
|
+
config: config
|
|
92
|
+
});
|
|
93
|
+
}
|
|
12
94
|
return new _safePlugin.SafePlugin({
|
|
13
|
-
key: limitedModePluginKey,
|
|
14
|
-
|
|
15
|
-
|
|
95
|
+
key: _pluginKey.limitedModePluginKey,
|
|
96
|
+
props: {
|
|
97
|
+
handleTextInput: function handleTextInput() {
|
|
98
|
+
var _detector;
|
|
99
|
+
(_detector = detector) === null || _detector === void 0 || _detector.measureInput();
|
|
100
|
+
|
|
101
|
+
// Never handle the input — this is measurement only.
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
view: function view(editorView) {
|
|
106
|
+
// No policy means the experiment is off: no observers, no per-keystroke measurement.
|
|
107
|
+
if (!policy) {
|
|
108
|
+
return {};
|
|
109
|
+
}
|
|
110
|
+
var startedAt = performance.now();
|
|
111
|
+
detector = (0, _latchDetector.createLatchDetector)({
|
|
112
|
+
policy: policy,
|
|
113
|
+
onLatchCriteriaMet: function onLatchCriteriaMet(details) {
|
|
114
|
+
var _api$analytics;
|
|
115
|
+
// Treatment arm only — the detector does not exist in control. See LimitedModeLatchedAEP.
|
|
116
|
+
//
|
|
117
|
+
// `details` is the policy's own snapshot of what it latched on, taken before it cleared
|
|
118
|
+
// its evidence buffers, so it reports the closing window rather than an empty one.
|
|
119
|
+
api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.fireAnalyticsEvent({
|
|
120
|
+
action: _analytics.ACTION.LIMITED_MODE_LATCHED,
|
|
121
|
+
actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
|
|
122
|
+
eventType: _analytics.EVENT_TYPE.OPERATIONAL,
|
|
123
|
+
attributes: _objectSpread(_objectSpread({
|
|
124
|
+
latched: true,
|
|
125
|
+
reason: details.reason,
|
|
126
|
+
firstWindowReason: details.firstWindowReason,
|
|
127
|
+
requiredConfirmations: details.requiredConfirmations,
|
|
128
|
+
documentAlreadyBreached: details.documentAlreadyBreached,
|
|
129
|
+
msFromFirstWindow: details.msFromFirstWindow,
|
|
130
|
+
latencyMedianMs: details.latencyMedianMs,
|
|
131
|
+
totalInputSamples: details.totalInputSamples,
|
|
132
|
+
totalSlowInputs: details.totalSlowInputs,
|
|
133
|
+
totalFreezes: details.totalFreezes,
|
|
134
|
+
nodeSize: editorView.state.doc.nodeSize
|
|
135
|
+
}, getDeviceHints()), {}, {
|
|
136
|
+
// Measured from the plugin view starting, which is a little later than the policy's
|
|
137
|
+
// own `timeToLatchMs` (it starts at plugin construction).
|
|
138
|
+
timeToLatch: performance.now() - startedAt
|
|
139
|
+
})
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// The policy already holds the latch; this transaction only prompts the plugin to
|
|
143
|
+
// re-read it, which is what notifies every consumer through shared state.
|
|
144
|
+
editorView.dispatch(editorView.state.tr.setMeta(_pluginKey.limitedModePluginKey, {
|
|
145
|
+
latchPolicyBreached: true
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
destroy: function destroy() {
|
|
151
|
+
var _detector2;
|
|
152
|
+
(_detector2 = detector) === null || _detector2 === void 0 || _detector2.destroy();
|
|
153
|
+
detector = undefined;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
16
156
|
},
|
|
17
157
|
state: {
|
|
18
158
|
init: function init(_config, editorState) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
159
|
+
if (!policy) {
|
|
160
|
+
return documentOnlyState(editorState.doc);
|
|
161
|
+
}
|
|
162
|
+
policy.evaluateDocument(editorState.doc);
|
|
163
|
+
return policyState(policy);
|
|
22
164
|
},
|
|
23
|
-
apply: function apply(tr, currentPluginState,
|
|
24
|
-
|
|
25
|
-
|
|
165
|
+
apply: function apply(tr, currentPluginState, oldState, _newState) {
|
|
166
|
+
var _tr$getMeta;
|
|
167
|
+
var documentReplaced = Boolean(tr.getMeta('replaceDocument'));
|
|
168
|
+
if (!policy) {
|
|
169
|
+
// Control arm, unchanged: skip the traversal once already breached, but always re-check
|
|
170
|
+
// when the document is replaced (e.g. live-to-live page navigation).
|
|
171
|
+
if (currentPluginState.documentSizeBreachesThreshold && !documentReplaced) {
|
|
172
|
+
return currentPluginState;
|
|
173
|
+
}
|
|
174
|
+
return documentOnlyState(tr.doc);
|
|
175
|
+
}
|
|
26
176
|
|
|
27
|
-
|
|
28
|
-
|
|
177
|
+
// The detector's latch arrives as a transaction so that plugin state stays a function of
|
|
178
|
+
// the transaction stream rather than of when `apply` happens to read the policy. Dev
|
|
179
|
+
// tooling dispatches the same meta to force limited mode by hand.
|
|
180
|
+
if ((_tr$getMeta = tr.getMeta(_pluginKey.limitedModePluginKey)) !== null && _tr$getMeta !== void 0 && _tr$getMeta.latchPolicyBreached) {
|
|
181
|
+
policy.latch();
|
|
29
182
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
183
|
+
|
|
184
|
+
// Only on replacement, never on an ordinary edit: the check walks the whole document, so
|
|
185
|
+
// running it per transaction is a full-document scan on every keystroke. The trade-off is
|
|
186
|
+
// that a document editing its way past the thresholds is not noticed until it next loads.
|
|
187
|
+
//
|
|
188
|
+
// Deliberately not skipped when limited mode is already on. Replacement is the one moment
|
|
189
|
+
// the document verdict can go *down* — live-to-live navigation onto a smaller page — so
|
|
190
|
+
// skipping it there is what would strand limited mode on forever. It costs one walk per
|
|
191
|
+
// page navigation, which is nothing next to the navigation itself.
|
|
192
|
+
if (documentReplaced) {
|
|
193
|
+
policy.evaluateDocument(tr.doc);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Report bulk work so the policy can discount it. The policy decides what counts as bulk;
|
|
197
|
+
// this only supplies the facts.
|
|
198
|
+
//
|
|
199
|
+
// Known gap: operations that are expensive but barely change document size — table
|
|
200
|
+
// resize, drag-and-drop moves (delete + insert nets to ~0), type-ahead — are not
|
|
201
|
+
// suppressed. `@atlaskit/insm` already tracks exactly these via `startHeavyTask`, but its
|
|
202
|
+
// public facade does not expose `runningHeavyTasks`, so there is no way to read them from
|
|
203
|
+
// here today. Closing that gap needs an accessor on the insm package.
|
|
204
|
+
if (tr.docChanged) {
|
|
205
|
+
var _detector3;
|
|
206
|
+
(_detector3 = detector) === null || _detector3 === void 0 || _detector3.noteDocumentChange({
|
|
207
|
+
nodeSizeDelta: tr.doc.nodeSize - oldState.doc.nodeSize,
|
|
208
|
+
isDocumentReplaced: documentReplaced
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
var next = policyState(policy);
|
|
212
|
+
|
|
213
|
+
// Keep the previous object when nothing changed, so shared-state diffing stays cheap and
|
|
214
|
+
// consumers are not notified for no reason.
|
|
215
|
+
return next.enabled === currentPluginState.enabled && next.documentSizeBreachesThreshold === currentPluginState.documentSizeBreachesThreshold ? currentPluginState : next;
|
|
33
216
|
}
|
|
34
217
|
}
|
|
35
218
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.limitedModePluginKey = void 0;
|
|
7
|
+
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
8
|
+
var limitedModePluginKey = exports.limitedModePluginKey = new _state.PluginKey('limitedModePlugin');
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createLatchDetector = void 0;
|
|
7
|
+
var _isPerformanceApiAvailable = require("@atlaskit/editor-common/is-performance-api-available");
|
|
8
|
+
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; } } }; }
|
|
9
|
+
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; } }
|
|
10
|
+
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
|
+
/**
|
|
12
|
+
* Wires browser performance signals into the supplied {@link LatchPolicy}.
|
|
13
|
+
*
|
|
14
|
+
* Holds no configuration and makes no decisions: it measures, and forwards. Everything tunable lives
|
|
15
|
+
* on the policy, including the clock it reads.
|
|
16
|
+
*
|
|
17
|
+
* The measurement mirrors `editor-plugin-base/src/pm-plugins/frozen-editor.ts`, which already runs an
|
|
18
|
+
* identical `longtask` observer and per-keystroke rAF measurement for every session to feed
|
|
19
|
+
* `ACTION.SLOW_INPUT` / `ACTION.BROWSER_FREEZE`. It is duplicated rather than shared so that this
|
|
20
|
+
* experiment stays self-contained inside the limited-mode plugin — no new cross-plugin dependency,
|
|
21
|
+
* and deleting it is a single-directory revert if the experiment does not ship. The marginal cost is
|
|
22
|
+
* one `performance.now()` and one `requestAnimationFrame` per keystroke.
|
|
23
|
+
*/
|
|
24
|
+
var createLatchDetector = exports.createLatchDetector = function createLatchDetector(_ref) {
|
|
25
|
+
var onLatchCriteriaMet = _ref.onLatchCriteriaMet,
|
|
26
|
+
policy = _ref.policy;
|
|
27
|
+
// One clock for the whole subsystem, so measured durations and the policy's windows agree.
|
|
28
|
+
var now = policy.now;
|
|
29
|
+
var observer;
|
|
30
|
+
var destroyed = false;
|
|
31
|
+
var handleEvaluation = function handleEvaluation(evaluation) {
|
|
32
|
+
if (evaluation !== 'latched') {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// One-way latch: the policy will never evaluate again, so stop paying for the observers.
|
|
37
|
+
teardownObservers();
|
|
38
|
+
var details = policy.getLatchDetails();
|
|
39
|
+
if (details) {
|
|
40
|
+
onLatchCriteriaMet(details);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function teardownObservers() {
|
|
44
|
+
var _observer;
|
|
45
|
+
(_observer = observer) === null || _observer === void 0 || _observer.disconnect();
|
|
46
|
+
observer = undefined;
|
|
47
|
+
}
|
|
48
|
+
if ((0, _isPerformanceApiAvailable.isPerformanceObserverLongTaskAvailable)()) {
|
|
49
|
+
try {
|
|
50
|
+
observer = new PerformanceObserver(function (list) {
|
|
51
|
+
var _iterator = _createForOfIteratorHelper(list.getEntries()),
|
|
52
|
+
_step;
|
|
53
|
+
try {
|
|
54
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
55
|
+
var entry = _step.value;
|
|
56
|
+
if (policy.isLatched()) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
handleEvaluation(policy.recordLongTask(entry.duration));
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
_iterator.e(err);
|
|
63
|
+
} finally {
|
|
64
|
+
_iterator.f();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
observer.observe({
|
|
68
|
+
entryTypes: ['longtask']
|
|
69
|
+
});
|
|
70
|
+
} catch (_unused) {
|
|
71
|
+
// `longtask` is unsupported in some browsers even when PerformanceObserver exists. The
|
|
72
|
+
// latency criterion alone is still a valid trigger, so carry on without freeze detection.
|
|
73
|
+
observer = undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
noteDocumentChange: function noteDocumentChange(_ref2) {
|
|
78
|
+
var nodeSizeDelta = _ref2.nodeSizeDelta,
|
|
79
|
+
isDocumentReplaced = _ref2.isDocumentReplaced;
|
|
80
|
+
if (destroyed || policy.isLatched()) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (isDocumentReplaced || policy.isBulkChange(nodeSizeDelta)) {
|
|
84
|
+
policy.suppress();
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
measureInput: function measureInput() {
|
|
88
|
+
if (destroyed || policy.isLatched() || !(0, _isPerformanceApiAvailable.isPerformanceAPIAvailable)()) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
var start = now();
|
|
92
|
+
|
|
93
|
+
// Runs after every handleTextInput and all resulting plugin work, but before paint — the
|
|
94
|
+
// same measurement point frozen-editor uses, so the numbers are comparable to existing
|
|
95
|
+
// SLOW_INPUT telemetry.
|
|
96
|
+
requestAnimationFrame(function () {
|
|
97
|
+
if (destroyed || policy.isLatched()) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
handleEvaluation(policy.recordInputLatency(now() - start));
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
destroy: function destroy() {
|
|
104
|
+
destroyed = true;
|
|
105
|
+
teardownObservers();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|