@codingame/monaco-vscode-welcome-service-override 36.2.7 → 37.1.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/index.js +1 -0
- package/package.json +3 -2
- package/vscode/src/vs/workbench/contrib/onboarding/browser/media/spotlight.css +5 -0
- package/vscode/src/vs/workbench/contrib/onboarding/browser/onboarding.contribution.js +37 -7
- package/vscode/src/vs/workbench/contrib/onboarding/browser/onboardingService.d.ts +2 -0
- package/vscode/src/vs/workbench/contrib/onboarding/browser/onboardingService.js +36 -5
- package/vscode/src/vs/workbench/contrib/onboarding/browser/sequence/runOnboardingStep.d.ts +17 -0
- package/vscode/src/vs/workbench/contrib/onboarding/browser/sequence/runOnboardingStep.js +32 -0
- package/vscode/src/vs/workbench/contrib/onboarding/browser/sequence/sequencePresentation.d.ts +10 -0
- package/vscode/src/vs/workbench/contrib/onboarding/browser/sequence/sequencePresentation.js +154 -0
- package/vscode/src/vs/workbench/contrib/onboarding/browser/spotlight/onboardingTarget.d.ts +7 -1
- package/vscode/src/vs/workbench/contrib/onboarding/browser/spotlight/onboardingTarget.js +5 -1
- package/vscode/src/vs/workbench/contrib/onboarding/browser/spotlight/spotlightOverlay.d.ts +13 -9
- package/vscode/src/vs/workbench/contrib/onboarding/browser/spotlight/spotlightOverlay.js +60 -15
- package/vscode/src/vs/workbench/contrib/onboarding/browser/spotlight/spotlightPresentation.d.ts +5 -1
- package/vscode/src/vs/workbench/contrib/onboarding/browser/spotlight/spotlightPresentation.js +222 -49
- package/vscode/src/vs/workbench/contrib/onboarding/browser/spotlight/spotlightTypes.d.ts +23 -5
- package/vscode/src/vs/workbench/contrib/onboarding/common/onboardingPresentation.d.ts +4 -0
- package/vscode/src/vs/workbench/contrib/onboarding/common/onboardingScenario.d.ts +9 -8
- package/vscode/src/vs/workbench/contrib/onboarding/common/onboardingScenarioService.d.ts +7 -0
- package/vscode/src/vs/workbench/contrib/onboarding/common/onboardingScenarioService.js +2 -1
- package/vscode/src/vs/workbench/contrib/onboarding/common/onboardingSequence.d.ts +45 -0
- package/vscode/src/vs/workbench/contrib/onboarding/common/onboardingSequence.js +29 -0
- package/vscode/src/vs/workbench/contrib/welcomeAgentSessions/browser/agentSessionsWelcome.contribution.d.ts +1 -0
- package/vscode/src/vs/workbench/contrib/welcomeAgentSessions/browser/agentSessionsWelcome.contribution.js +158 -0
- package/vscode/src/vs/workbench/contrib/welcomeOnboarding/browser/media/variationA.css +2 -149
- package/vscode/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.d.ts +5 -5
- package/vscode/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.js +61 -131
- package/vscode/src/vs/workbench/contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.js +1 -1
- package/vscode/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.js +10 -10
- package/vscode/src/vs/workbench/contrib/welcomeViews/common/viewsWelcomeContribution.js +2 -2
- package/vscode/src/vs/workbench/contrib/welcomeViews/common/viewsWelcomeExtensionPoint.js +8 -8
package/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import './vscode/src/vs/workbench/contrib/onboarding/browser/onboarding.contribu
|
|
|
8
8
|
import './vscode/src/vs/workbench/contrib/welcomeViews/common/viewsWelcome.contribution.js';
|
|
9
9
|
import './vscode/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.js';
|
|
10
10
|
import './vscode/src/vs/workbench/contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.js';
|
|
11
|
+
import './vscode/src/vs/workbench/contrib/welcomeAgentSessions/browser/agentSessionsWelcome.contribution.js';
|
|
11
12
|
|
|
12
13
|
function getServiceOverride() {
|
|
13
14
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codingame/monaco-vscode-welcome-service-override",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "37.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "VSCode public API plugged on the monaco editor - welcome service-override",
|
|
6
6
|
"keywords": [],
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
"type": "module",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@codingame/monaco-vscode-api": "
|
|
18
|
+
"@codingame/monaco-vscode-api": "37.1.0",
|
|
19
|
+
"@codingame/monaco-vscode-katex-common": "37.1.0"
|
|
19
20
|
},
|
|
20
21
|
"main": "index.js",
|
|
21
22
|
"module": "index.js",
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
z-index: 2600;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/* Expanded pickers and mobile sheets remain above the spotlight blocker. */
|
|
17
|
+
.spotlight-overlay.target-overlay-visible {
|
|
18
|
+
z-index: 1999;
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
/* Full-screen click blocker. Transparent; swallows interaction outside the hole. */
|
|
17
22
|
.spotlight-blocker {
|
|
18
23
|
position: fixed;
|
|
@@ -12,21 +12,34 @@ import { workbenchConfigurationNodeBase } from '@codingame/monaco-vscode-api/vsc
|
|
|
12
12
|
import { registerWorkbenchContribution2, WorkbenchPhase } from '@codingame/monaco-vscode-api/vscode/vs/workbench/common/contributions';
|
|
13
13
|
import { onboardingPresentationRegistry } from '../common/onboardingPresentation.js';
|
|
14
14
|
import { onboardingScenarioRegistry } from '../common/onboardingRegistry.js';
|
|
15
|
-
import {
|
|
15
|
+
import { onboardingSequenceStepPresentationRegistry } from '../common/onboardingSequence.js';
|
|
16
|
+
import { ONBOARDING_DEVELOPER_MODE_VARIATIONS_CONFIG, ONBOARDING_DEVELOPER_MODE_CONFIG, ONBOARDING_ENABLED_CONFIG } from '../common/onboardingScenarioService.js';
|
|
16
17
|
import { IOnboardingScenarioService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/onboarding/common/onboardingScenarioService.service';
|
|
17
18
|
import './onboardingService.js';
|
|
19
|
+
import { RunOnboardingStepPresentation } from './sequence/runOnboardingStep.js';
|
|
20
|
+
import { OnboardingSequencePresentation } from './sequence/sequencePresentation.js';
|
|
18
21
|
import { SpotlightPresentation } from './spotlight/spotlightPresentation.js';
|
|
19
22
|
|
|
20
23
|
const configurationRegistry = ( Registry.as(Extensions.Configuration));
|
|
21
24
|
function buildDeveloperModeConfigurationNode() {
|
|
22
25
|
const properties = {};
|
|
23
26
|
const defaultValue = {};
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
const variationProperties = {};
|
|
28
|
+
const variationDefaultValue = {};
|
|
29
|
+
for (const scenario of [...onboardingScenarioRegistry.getScenarios()].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
30
|
+
properties[scenario.id] = {
|
|
26
31
|
type: "boolean",
|
|
27
32
|
default: false
|
|
28
33
|
};
|
|
29
|
-
defaultValue[id] = false;
|
|
34
|
+
defaultValue[scenario.id] = false;
|
|
35
|
+
if (scenario.developerModeVariations?.length) {
|
|
36
|
+
variationProperties[scenario.id] = {
|
|
37
|
+
type: "string",
|
|
38
|
+
default: "",
|
|
39
|
+
enum: ["", ...scenario.developerModeVariations]
|
|
40
|
+
};
|
|
41
|
+
variationDefaultValue[scenario.id] = "";
|
|
42
|
+
}
|
|
30
43
|
}
|
|
31
44
|
return {
|
|
32
45
|
...workbenchConfigurationNodeBase,
|
|
@@ -40,10 +53,23 @@ function buildDeveloperModeConfigurationNode() {
|
|
|
40
53
|
},
|
|
41
54
|
tags: ["experimental"],
|
|
42
55
|
description: ( localize(
|
|
43
|
-
|
|
56
|
+
14868,
|
|
44
57
|
"Map of onboarding scenario/tour id to whether developer mode is enabled for it. When enabled for a scenario, that onboarding tour ignores usage-based eligibility checks (such as how many sessions you have started), previously persisted shown state, and any linked experiment (so it is shown even if the experiment is not running or you are in the control group). It does not override the {0} setting. The tour is still shown at most once per window session, so reload the window to show it again.",
|
|
45
58
|
`\`#${ONBOARDING_ENABLED_CONFIG}#\``
|
|
46
59
|
))
|
|
60
|
+
},
|
|
61
|
+
[ONBOARDING_DEVELOPER_MODE_VARIATIONS_CONFIG]: {
|
|
62
|
+
type: "object",
|
|
63
|
+
default: variationDefaultValue,
|
|
64
|
+
properties: variationProperties,
|
|
65
|
+
additionalProperties: {
|
|
66
|
+
type: "string"
|
|
67
|
+
},
|
|
68
|
+
tags: ["experimental"],
|
|
69
|
+
description: ( localize(
|
|
70
|
+
14869,
|
|
71
|
+
"Map of onboarding scenario/tour id to the variation used while developer mode is enabled for that scenario. An empty value uses the experiment-selected or default variation."
|
|
72
|
+
))
|
|
47
73
|
}
|
|
48
74
|
}
|
|
49
75
|
};
|
|
@@ -56,7 +82,7 @@ configurationRegistry.registerConfiguration({
|
|
|
56
82
|
type: "boolean",
|
|
57
83
|
default: true,
|
|
58
84
|
description: ( localize(
|
|
59
|
-
|
|
85
|
+
14870,
|
|
60
86
|
"When enabled, onboarding tours and hints may appear automatically to highlight features. Disabling this does not affect tours you start manually."
|
|
61
87
|
))
|
|
62
88
|
}
|
|
@@ -83,6 +109,10 @@ let OnboardingContribution = class OnboardingContribution extends Disposable {
|
|
|
83
109
|
);
|
|
84
110
|
const spotlight = this._register(instantiationService.createInstance(SpotlightPresentation));
|
|
85
111
|
this._register(onboardingPresentationRegistry.register(spotlight));
|
|
112
|
+
this._register(onboardingSequenceStepPresentationRegistry.register(spotlight));
|
|
113
|
+
const sequence = this._register(( new OnboardingSequencePresentation()));
|
|
114
|
+
this._register(onboardingPresentationRegistry.register(sequence));
|
|
115
|
+
this._register(onboardingSequenceStepPresentationRegistry.register(( new RunOnboardingStepPresentation())));
|
|
86
116
|
onboardingService.start();
|
|
87
117
|
}
|
|
88
118
|
};
|
|
@@ -96,7 +126,7 @@ registerAction2(class extends Action2 {
|
|
|
96
126
|
constructor() {
|
|
97
127
|
super({
|
|
98
128
|
id: "workbench.action.onboarding.resetShownState",
|
|
99
|
-
title: ( localize2(
|
|
129
|
+
title: ( localize2(14871, "Reset Onboarding Shown State")),
|
|
100
130
|
category: Categories.Developer,
|
|
101
131
|
f1: true
|
|
102
132
|
});
|
|
@@ -93,6 +93,8 @@ export declare class OnboardingScenarioService extends Disposable implements IOn
|
|
|
93
93
|
private _pump;
|
|
94
94
|
private _doPump;
|
|
95
95
|
private _runPresentation;
|
|
96
|
+
/** Emit an impression when a presentation has rendered visible onboarding UI. */
|
|
97
|
+
private _reportShown;
|
|
96
98
|
/** Emit per-tour telemetry. Only called when a tour was actually shown. */
|
|
97
99
|
private _reportOutcome;
|
|
98
100
|
private _registerTriggerListeners;
|
|
@@ -61,7 +61,10 @@ let OnboardingScenarioService = class OnboardingScenarioService extends Disposab
|
|
|
61
61
|
this._openedAssignmentContextIds = this._loadOpenedIds();
|
|
62
62
|
this.assignmentService.addTelemetryAssignmentFilter({
|
|
63
63
|
id: "onboarding",
|
|
64
|
-
exclude: assignment =>
|
|
64
|
+
exclude: assignment => {
|
|
65
|
+
const variant = getAssignmentContextVariant(assignment);
|
|
66
|
+
return variant.startsWith(ONBOARDING_ASSIGNMENT_CONTEXT_PREFIX) && !( this._openedAssignmentContextIds.has(variant));
|
|
67
|
+
},
|
|
65
68
|
onDidChange: this._onDidChangeOpenedIds.event
|
|
66
69
|
});
|
|
67
70
|
this._register(this.lifecycleService.onWillShutdown(() => this._stop()));
|
|
@@ -136,10 +139,19 @@ let OnboardingScenarioService = class OnboardingScenarioService extends Disposab
|
|
|
136
139
|
claimedSeenKeys.add(this._seenKey(scenario));
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
const eligibleScenarios = ( onboardingScenarioRegistry.getScenarios().map((scenario, registrationIndex) => ({
|
|
143
|
+
scenario,
|
|
144
|
+
registrationIndex
|
|
145
|
+
}))).filter((
|
|
146
|
+
{
|
|
147
|
+
scenario
|
|
142
148
|
}
|
|
149
|
+
) => this._isAutoEligible(scenario)).sort(
|
|
150
|
+
(a, b) => (b.scenario.priority ?? 0) - (a.scenario.priority ?? 0) || a.registrationIndex - b.registrationIndex
|
|
151
|
+
);
|
|
152
|
+
for (const {
|
|
153
|
+
scenario
|
|
154
|
+
} of eligibleScenarios) {
|
|
143
155
|
const seenKey = this._seenKey(scenario);
|
|
144
156
|
if (!scenario.repeatable && ( claimedSeenKeys.has(seenKey))) {
|
|
145
157
|
continue;
|
|
@@ -242,10 +254,17 @@ let OnboardingScenarioService = class OnboardingScenarioService extends Disposab
|
|
|
242
254
|
const abort = ( new Emitter());
|
|
243
255
|
this._activeAbort = abort;
|
|
244
256
|
const startTime = Date.now();
|
|
257
|
+
let didReportShown = false;
|
|
245
258
|
try {
|
|
246
259
|
const result = await presentation.run(scenario, {
|
|
247
260
|
targetWindow: mainWindow,
|
|
248
|
-
onAbort: abort.event
|
|
261
|
+
onAbort: abort.event,
|
|
262
|
+
onDidShow: () => {
|
|
263
|
+
if (!didReportShown) {
|
|
264
|
+
didReportShown = true;
|
|
265
|
+
this._reportShown(scenario);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
249
268
|
});
|
|
250
269
|
this._recordOutcome(this._seenKey(scenario), result.outcome);
|
|
251
270
|
if (result.shown) {
|
|
@@ -257,6 +276,14 @@ let OnboardingScenarioService = class OnboardingScenarioService extends Disposab
|
|
|
257
276
|
abort.dispose();
|
|
258
277
|
}
|
|
259
278
|
}
|
|
279
|
+
_reportShown(scenario) {
|
|
280
|
+
const experimentState = scenario.experiment ? this._experimentStates.get(scenario.id) : undefined;
|
|
281
|
+
this.telemetryService.publicLog2("onboarding.scenarioShown", {
|
|
282
|
+
scenarioId: scenario.id,
|
|
283
|
+
experimentActive: experimentState?.active === true,
|
|
284
|
+
experimentAssignmentContextId: experimentState?.active ? experimentState.assignmentContextId : undefined
|
|
285
|
+
});
|
|
286
|
+
}
|
|
260
287
|
_reportOutcome(scenario, result, durationMs) {
|
|
261
288
|
const experimentActive = !!scenario.experiment && this._experimentStates.get(scenario.id)?.active === true;
|
|
262
289
|
this.telemetryService.publicLog2("onboarding.scenarioOutcome", {
|
|
@@ -374,5 +401,9 @@ let OnboardingScenarioService = class OnboardingScenarioService extends Disposab
|
|
|
374
401
|
}
|
|
375
402
|
};
|
|
376
403
|
OnboardingScenarioService = OnboardingScenarioService_1 = ( __decorate([( __param(0, IStorageService)), ( __param(1, IContextKeyService)), ( __param(2, IConfigurationService)), ( __param(3, ILifecycleService)), ( __param(4, IWorkbenchAssignmentService)), ( __param(5, ITelemetryService))], OnboardingScenarioService));
|
|
404
|
+
function getAssignmentContextVariant(assignment) {
|
|
405
|
+
const separatorIndex = assignment.lastIndexOf(":");
|
|
406
|
+
return separatorIndex === -1 ? assignment : assignment.slice(0, separatorIndex);
|
|
407
|
+
}
|
|
377
408
|
|
|
378
409
|
export { OnboardingScenarioService };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CancellationToken } from "@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation";
|
|
2
|
+
import { IOnboardingSequenceStep, IOnboardingSequenceStepContext, IOnboardingSequenceStepPresentation, IOnboardingSequenceStepResult } from "../../common/onboardingSequence.js";
|
|
3
|
+
export declare const RUN_ONBOARDING_STEP_KIND = "run";
|
|
4
|
+
export interface IRunOnboardingStepResult {
|
|
5
|
+
readonly shown?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** Callback payload for a cancellable non-visual sequence step. */
|
|
8
|
+
export interface IRunOnboardingStepPayload {
|
|
9
|
+
readonly run: (token: CancellationToken) => Promise<IRunOnboardingStepResult | void> | IRunOnboardingStepResult | void;
|
|
10
|
+
}
|
|
11
|
+
/** Executes a run-step callback once and continues after reported errors. */
|
|
12
|
+
export declare class RunOnboardingStepPresentation implements IOnboardingSequenceStepPresentation {
|
|
13
|
+
readonly kind = "run";
|
|
14
|
+
readonly countsAsVisualStep = false;
|
|
15
|
+
readonly runOnce = true;
|
|
16
|
+
runStep(step: IOnboardingSequenceStep, context: IOnboardingSequenceStepContext): Promise<IOnboardingSequenceStepResult>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
import { raceCancellation } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
|
|
3
|
+
import { onUnexpectedError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
|
|
4
|
+
|
|
5
|
+
const RUN_ONBOARDING_STEP_KIND = 'run';
|
|
6
|
+
class RunOnboardingStepPresentation {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.kind = RUN_ONBOARDING_STEP_KIND;
|
|
9
|
+
this.countsAsVisualStep = false;
|
|
10
|
+
this.runOnce = true;
|
|
11
|
+
}
|
|
12
|
+
async runStep(step, context) {
|
|
13
|
+
if (context.cancellationToken.isCancellationRequested) {
|
|
14
|
+
return { action: 'abort', shown: false };
|
|
15
|
+
}
|
|
16
|
+
let result = undefined;
|
|
17
|
+
try {
|
|
18
|
+
const payload = step.payload;
|
|
19
|
+
result = await raceCancellation(Promise.resolve(payload.run(context.cancellationToken)), context.cancellationToken);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (!context.cancellationToken.isCancellationRequested) {
|
|
23
|
+
onUnexpectedError(error);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return context.cancellationToken.isCancellationRequested
|
|
27
|
+
? { action: 'abort', shown: false }
|
|
28
|
+
: { action: 'next', shown: result?.shown === true };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { RUN_ONBOARDING_STEP_KIND, RunOnboardingStepPresentation };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Disposable } from "@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle";
|
|
2
|
+
import { IOnboardingPresentation, IOnboardingRunContext } from "../../common/onboardingPresentation.js";
|
|
3
|
+
import { IOnboardingRunResult, IOnboardingScenario } from "../../common/onboardingScenario.js";
|
|
4
|
+
/** Runs a heterogeneous sequence while leaving each step's rendering to its registered kind. */
|
|
5
|
+
export declare class OnboardingSequencePresentation extends Disposable implements IOnboardingPresentation {
|
|
6
|
+
readonly kind = "sequence";
|
|
7
|
+
run(scenario: IOnboardingScenario, context: IOnboardingRunContext): Promise<IOnboardingRunResult>;
|
|
8
|
+
private _presentation;
|
|
9
|
+
private _visualStepIndex;
|
|
10
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
|
|
2
|
+
import { CancellationTokenSource } from '@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation';
|
|
3
|
+
import { onUnexpectedError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
|
|
4
|
+
import { Disposable, DisposableStore } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
|
|
5
|
+
import { OnboardingDismissReason, OnboardingOutcome } from '../../common/onboardingScenario.js';
|
|
6
|
+
import { ONBOARDING_SEQUENCE_PRESENTATION_KIND, onboardingSequenceStepPresentationRegistry } from '../../common/onboardingSequence.js';
|
|
7
|
+
|
|
8
|
+
class OnboardingSequencePresentation extends Disposable {
|
|
9
|
+
constructor() {
|
|
10
|
+
super(...arguments);
|
|
11
|
+
this.kind = ONBOARDING_SEQUENCE_PRESENTATION_KIND;
|
|
12
|
+
}
|
|
13
|
+
async run(scenario, context) {
|
|
14
|
+
const steps = scenario.presentation.payload?.steps ?? [];
|
|
15
|
+
if (steps.length === 0) {
|
|
16
|
+
return {
|
|
17
|
+
outcome: OnboardingOutcome.Completed,
|
|
18
|
+
shown: false,
|
|
19
|
+
dismissReason: OnboardingDismissReason.Completed,
|
|
20
|
+
lastStepIndex: 0,
|
|
21
|
+
stepCount: 0
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const store = ( new DisposableStore());
|
|
25
|
+
try {
|
|
26
|
+
const cancellation = store.add(( new CancellationTokenSource()));
|
|
27
|
+
store.add(context.onAbort(() => cancellation.cancel()));
|
|
28
|
+
const skippedVisualSteps = ( new Set());
|
|
29
|
+
const shownVisualSteps = ( new Set());
|
|
30
|
+
const executedRunOnceSteps = ( new Set());
|
|
31
|
+
const visualStepCount = steps.reduce(
|
|
32
|
+
(count, step) => count + (this._presentation(step)?.countsAsVisualStep ? 1 : 0),
|
|
33
|
+
0
|
|
34
|
+
);
|
|
35
|
+
let index = 0;
|
|
36
|
+
let direction = 1;
|
|
37
|
+
let shown = false;
|
|
38
|
+
let lastStepIndex = 0;
|
|
39
|
+
let dismissReason = OnboardingDismissReason.Completed;
|
|
40
|
+
while (index >= 0 && index < steps.length && !cancellation.token.isCancellationRequested) {
|
|
41
|
+
const step = steps[index];
|
|
42
|
+
const presentation = this._presentation(step);
|
|
43
|
+
if (!presentation) {
|
|
44
|
+
onUnexpectedError(( new Error(`No onboarding sequence step presentation registered for '${step.kind}'.`)));
|
|
45
|
+
return {
|
|
46
|
+
outcome: OnboardingOutcome.Aborted,
|
|
47
|
+
shown,
|
|
48
|
+
dismissReason: OnboardingDismissReason.Aborted,
|
|
49
|
+
lastStepIndex,
|
|
50
|
+
stepCount: steps.length
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (!presentation.countsAsVisualStep && direction === -1) {
|
|
54
|
+
index--;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (presentation.runOnce && ( executedRunOnceSteps.has(index))) {
|
|
58
|
+
index += direction;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (presentation.runOnce) {
|
|
62
|
+
executedRunOnceSteps.add(index);
|
|
63
|
+
}
|
|
64
|
+
if (presentation.countsAsVisualStep) {
|
|
65
|
+
skippedVisualSteps.delete(index);
|
|
66
|
+
}
|
|
67
|
+
const visualStepIndex = this._visualStepIndex(steps, index, skippedVisualSteps);
|
|
68
|
+
const currentVisualStepCount = visualStepCount - skippedVisualSteps.size;
|
|
69
|
+
if (!presentation.countsAsVisualStep) {
|
|
70
|
+
lastStepIndex = Math.max(lastStepIndex, index);
|
|
71
|
+
}
|
|
72
|
+
const result = await presentation.runStep(step, {
|
|
73
|
+
...context,
|
|
74
|
+
cancellationToken: cancellation.token,
|
|
75
|
+
stepIndex: index,
|
|
76
|
+
visualStepIndex,
|
|
77
|
+
visualStepCount: currentVisualStepCount,
|
|
78
|
+
canGoBack: ( Array.from(shownVisualSteps).some(stepIndex => stepIndex < index)),
|
|
79
|
+
isLastVisualStep: visualStepIndex === currentVisualStepCount - 1
|
|
80
|
+
});
|
|
81
|
+
if (cancellation.token.isCancellationRequested || result.action === "abort") {
|
|
82
|
+
return {
|
|
83
|
+
outcome: OnboardingOutcome.Aborted,
|
|
84
|
+
shown,
|
|
85
|
+
dismissReason: OnboardingDismissReason.Aborted,
|
|
86
|
+
lastStepIndex,
|
|
87
|
+
stepCount: steps.length
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (result.shown) {
|
|
91
|
+
shown = true;
|
|
92
|
+
lastStepIndex = Math.max(lastStepIndex, index);
|
|
93
|
+
if (presentation.countsAsVisualStep) {
|
|
94
|
+
shownVisualSteps.add(index);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
switch (result.action) {
|
|
98
|
+
case "next":
|
|
99
|
+
dismissReason = result.dismissReason ?? dismissReason;
|
|
100
|
+
direction = 1;
|
|
101
|
+
index++;
|
|
102
|
+
break;
|
|
103
|
+
case "back":
|
|
104
|
+
direction = -1;
|
|
105
|
+
index--;
|
|
106
|
+
break;
|
|
107
|
+
case "skipStep":
|
|
108
|
+
if (presentation.countsAsVisualStep) {
|
|
109
|
+
skippedVisualSteps.add(index);
|
|
110
|
+
}
|
|
111
|
+
index += direction;
|
|
112
|
+
break;
|
|
113
|
+
case "skipSequence":
|
|
114
|
+
return {
|
|
115
|
+
outcome: OnboardingOutcome.Skipped,
|
|
116
|
+
shown,
|
|
117
|
+
dismissReason: result.dismissReason ?? OnboardingDismissReason.SkipButton,
|
|
118
|
+
lastStepIndex,
|
|
119
|
+
stepCount: steps.length
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return cancellation.token.isCancellationRequested ? {
|
|
124
|
+
outcome: OnboardingOutcome.Aborted,
|
|
125
|
+
shown,
|
|
126
|
+
dismissReason: OnboardingDismissReason.Aborted,
|
|
127
|
+
lastStepIndex,
|
|
128
|
+
stepCount: steps.length
|
|
129
|
+
} : {
|
|
130
|
+
outcome: OnboardingOutcome.Completed,
|
|
131
|
+
shown,
|
|
132
|
+
dismissReason,
|
|
133
|
+
lastStepIndex,
|
|
134
|
+
stepCount: steps.length
|
|
135
|
+
};
|
|
136
|
+
} finally {
|
|
137
|
+
store.dispose();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
_presentation(step) {
|
|
141
|
+
return onboardingSequenceStepPresentationRegistry.get(step.kind);
|
|
142
|
+
}
|
|
143
|
+
_visualStepIndex(steps, index, skippedVisualSteps) {
|
|
144
|
+
let visualStepIndex = 0;
|
|
145
|
+
for (let stepIndex = 0; stepIndex < index; stepIndex++) {
|
|
146
|
+
if (this._presentation(steps[stepIndex])?.countsAsVisualStep && !( skippedVisualSteps.has(stepIndex))) {
|
|
147
|
+
visualStepIndex++;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return visualStepIndex;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export { OnboardingSequencePresentation };
|
|
@@ -8,12 +8,18 @@ import { IDisposable } from "@codingame/monaco-vscode-api/vscode/vs/base/common/
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const ONBOARDING_TARGET_ATTR = "data-onboarding-id";
|
|
10
10
|
export declare const ONBOARDING_TARGET_PULSE_CLASS = "onboarding-target-pulse";
|
|
11
|
+
export interface IOnboardingTargetOptions {
|
|
12
|
+
/** Opens or expands the target before its spotlight step is shown. */
|
|
13
|
+
readonly open?: () => Promise<void> | void;
|
|
14
|
+
}
|
|
11
15
|
/**
|
|
12
16
|
* Marks `element` as the onboarding target identified by `id`.
|
|
13
17
|
*
|
|
14
18
|
* @returns A disposable that removes the attribute again.
|
|
15
19
|
*/
|
|
16
|
-
export declare function markOnboardingTarget(element: HTMLElement, id: string): IDisposable;
|
|
20
|
+
export declare function markOnboardingTarget(element: HTMLElement, id: string, options?: IOnboardingTargetOptions): IDisposable;
|
|
21
|
+
/** Opens or expands a target through the behavior registered by its owner. */
|
|
22
|
+
export declare function openOnboardingTarget(element: HTMLElement): Promise<void> | void;
|
|
17
23
|
/**
|
|
18
24
|
* Applies the standard onboarding pulse treatment to `element`.
|
|
19
25
|
*
|
|
@@ -5,6 +5,10 @@ import * as onboardingTarget from '../media/onboardingTarget.css';
|
|
|
5
5
|
|
|
6
6
|
registerCss(onboardingTarget);
|
|
7
7
|
const ONBOARDING_TARGET_ATTR = "data-onboarding-id";
|
|
8
|
+
const onboardingTargetRegistrations = ( new WeakMap());
|
|
9
|
+
function openOnboardingTarget(element) {
|
|
10
|
+
return onboardingTargetRegistrations.get(element)?.options.open?.();
|
|
11
|
+
}
|
|
8
12
|
function findOnboardingTarget(targetWindow, id) {
|
|
9
13
|
const selector = `[${ONBOARDING_TARGET_ATTR}="${CSS.escape(id)}"]`;
|
|
10
14
|
const targets = Array.from(targetWindow.document.querySelectorAll(selector));
|
|
@@ -22,4 +26,4 @@ function isVisibleOnboardingTarget(targetWindow, target) {
|
|
|
22
26
|
return rect.width > 0 && rect.height > 0;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
export { ONBOARDING_TARGET_ATTR, findOnboardingTarget };
|
|
29
|
+
export { ONBOARDING_TARGET_ATTR, findOnboardingTarget, openOnboardingTarget };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Disposable } from "@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle";
|
|
2
2
|
import { Event } from "@codingame/monaco-vscode-api/vscode/vs/base/common/event";
|
|
3
3
|
import { IMarkdownString } from "@codingame/monaco-vscode-api/vscode/vs/base/common/htmlContent";
|
|
4
|
-
import { SpotlightPlacement } from "./spotlightTypes.js";
|
|
4
|
+
import { SpotlightPlacement, SpotlightTargetClickBehavior } from "./spotlightTypes.js";
|
|
5
5
|
import { OnboardingDismissReason } from "../../common/onboardingScenario.js";
|
|
6
6
|
/** How the user advanced to the next step. */
|
|
7
7
|
export type SpotlightAdvanceSource = "button" | "target";
|
|
@@ -11,9 +11,11 @@ export type SpotlightSkipReason = OnboardingDismissReason.SkipButton | Onboardin
|
|
|
11
11
|
export interface ISpotlightContent {
|
|
12
12
|
readonly title: string;
|
|
13
13
|
readonly description: string | IMarkdownString;
|
|
14
|
+
/** Localized primary button label, replacing the default Next or Done. */
|
|
15
|
+
readonly nextButtonLabel?: string;
|
|
14
16
|
/** Zero-based index of the current step. */
|
|
15
17
|
readonly stepIndex: number;
|
|
16
|
-
/** Total number of steps in the tour. */
|
|
18
|
+
/** Total number of steps in the tour. Progress is displayed only for multi-step tours. */
|
|
17
19
|
readonly stepCount: number;
|
|
18
20
|
/** Whether a "Back" action should be offered. */
|
|
19
21
|
readonly canGoBack: boolean;
|
|
@@ -25,12 +27,10 @@ export interface ISpotlightShowOptions {
|
|
|
25
27
|
readonly placement?: SpotlightPlacement;
|
|
26
28
|
readonly allowTargetInteraction?: boolean;
|
|
27
29
|
readonly padding?: number;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
*/
|
|
33
|
-
readonly advanceOnTargetClick?: boolean;
|
|
30
|
+
readonly hideNext?: boolean;
|
|
31
|
+
readonly targetOverlayVisible?: boolean;
|
|
32
|
+
/** Advances on target activation; `advanceOnly` consumes the activation without running its action. */
|
|
33
|
+
readonly advanceOnTargetClick?: SpotlightTargetClickBehavior;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* A pure-DOM spotlight overlay: dims the window, cuts a highlight hole around a
|
|
@@ -63,11 +63,14 @@ export declare class SpotlightOverlay extends Disposable {
|
|
|
63
63
|
readonly onDidSkip: Event<SpotlightSkipReason>;
|
|
64
64
|
private _target;
|
|
65
65
|
private _options;
|
|
66
|
+
private _hasShown;
|
|
66
67
|
private _previousFocus;
|
|
67
68
|
private _scheduledLayout;
|
|
68
69
|
constructor(_container: HTMLElement, _resizeObserverCtor?: typeof ResizeObserver);
|
|
69
70
|
/** Show `content` spotlighting `target`. */
|
|
70
71
|
show(target: HTMLElement, content: ISpotlightContent, options?: ISpotlightShowOptions): void;
|
|
72
|
+
/** Hide the current step while another target is being resolved. */
|
|
73
|
+
hide(): void;
|
|
71
74
|
/** Recompute the hole and callout positions for the current target. */
|
|
72
75
|
layout(): void;
|
|
73
76
|
private _layoutBlocker;
|
|
@@ -83,13 +86,14 @@ export declare class SpotlightOverlay extends Disposable {
|
|
|
83
86
|
private _trapFocus;
|
|
84
87
|
/**
|
|
85
88
|
* The focusable elements participating in the focus trap, in DOM order: the
|
|
86
|
-
* spotlighted target (when the
|
|
89
|
+
* spotlighted target (when it is interactive or the Next button is hidden), then any
|
|
87
90
|
* interactive content in the (possibly markdown) description, then the visible
|
|
88
91
|
* action buttons. Including the target keeps the spotlighted control
|
|
89
92
|
* keyboard-reachable, and querying the description keeps markdown links
|
|
90
93
|
* reachable despite `aria-modal`.
|
|
91
94
|
*/
|
|
92
95
|
private _collectFocusable;
|
|
96
|
+
private _isTabbable;
|
|
93
97
|
scheduleLayout(): void;
|
|
94
98
|
private _restoreFocus;
|
|
95
99
|
dispose(): void;
|