@theia/getting-started 1.76.0-next.16 → 1.76.0-next.18
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/README.md +44 -0
- package/lib/browser/getting-started-frontend-module.d.ts.map +1 -1
- package/lib/browser/getting-started-frontend-module.js +3 -0
- package/lib/browser/getting-started-frontend-module.js.map +1 -1
- package/lib/browser/getting-started-widget.d.ts +0 -7
- package/lib/browser/getting-started-widget.d.ts.map +1 -1
- package/lib/browser/getting-started-widget.js +1 -29
- package/lib/browser/getting-started-widget.js.map +1 -1
- package/lib/browser/walkthrough-section.d.ts +2 -2
- package/lib/browser/walkthrough-section.d.ts.map +1 -1
- package/lib/browser/walkthrough-section.js +7 -5
- package/lib/browser/walkthrough-section.js.map +1 -1
- package/lib/browser/walkthrough-section.spec.js +17 -1
- package/lib/browser/walkthrough-section.spec.js.map +1 -1
- package/lib/browser/walkthrough-service.d.ts +26 -5
- package/lib/browser/walkthrough-service.d.ts.map +1 -1
- package/lib/browser/walkthrough-service.js +77 -22
- package/lib/browser/walkthrough-service.js.map +1 -1
- package/lib/browser/walkthrough-service.spec.js +173 -57
- package/lib/browser/walkthrough-service.spec.js.map +1 -1
- package/lib/common/walkthrough-provider.d.ts +22 -0
- package/lib/common/walkthrough-provider.d.ts.map +1 -0
- package/lib/common/walkthrough-provider.js +27 -0
- package/lib/common/walkthrough-provider.js.map +1 -0
- package/lib/common/walkthrough-types.d.ts +18 -5
- package/lib/common/walkthrough-types.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/browser/getting-started-frontend-module.ts +3 -0
- package/src/browser/getting-started-widget.tsx +31 -101
- package/src/browser/style/index.css +69 -35
- package/src/browser/walkthrough-section.spec.tsx +29 -1
- package/src/browser/walkthrough-section.tsx +7 -5
- package/src/browser/walkthrough-service.spec.ts +207 -57
- package/src/browser/walkthrough-service.ts +82 -25
- package/src/common/walkthrough-provider.ts +38 -0
- package/src/common/walkthrough-types.ts +20 -5
|
@@ -21,14 +21,16 @@ import { StorageService } from '@theia/core/lib/browser/storage-service';
|
|
|
21
21
|
import { CommandRegistry } from '@theia/core/lib/common/command';
|
|
22
22
|
import { PreferenceService } from '@theia/core/lib/common/preferences';
|
|
23
23
|
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
|
24
|
-
import { DeployedPlugin, PluginIdentifiers, PluginMetadata
|
|
24
|
+
import { DeployedPlugin, PluginIdentifiers, PluginMetadata } from '@theia/plugin-ext/lib/common/plugin-protocol';
|
|
25
25
|
|
|
26
26
|
import { OpenerService, open } from '@theia/core/lib/browser/opener-service';
|
|
27
|
+
import { ContributionProvider } from '@theia/core/lib/common/contribution-provider';
|
|
27
28
|
import { ILogger } from '@theia/core/lib/common/logger';
|
|
28
29
|
import { MessageService } from '@theia/core/lib/common/message-service';
|
|
29
30
|
import { nls } from '@theia/core/lib/common/nls';
|
|
30
31
|
import { URI } from '@theia/core/lib/common/uri';
|
|
31
|
-
import { Walkthrough, WalkthroughStep } from '../common/walkthrough-types';
|
|
32
|
+
import { Walkthrough, WalkthroughDefinition, WalkthroughStep, WalkthroughStepDefinition } from '../common/walkthrough-types';
|
|
33
|
+
import { WalkthroughProvider } from '../common/walkthrough-provider';
|
|
32
34
|
import { GettingStartedPreferences } from '../common/getting-started-preferences';
|
|
33
35
|
import { WalkthroughCommands } from '../common/walkthrough-commands';
|
|
34
36
|
|
|
@@ -77,6 +79,9 @@ export class WalkthroughService implements Disposable {
|
|
|
77
79
|
@inject(WalkthroughViewEventSource)
|
|
78
80
|
protected readonly viewEventSource: WalkthroughViewEventSource;
|
|
79
81
|
|
|
82
|
+
@inject(ContributionProvider) @named(WalkthroughProvider)
|
|
83
|
+
protected readonly walkthroughProviders: ContributionProvider<WalkthroughProvider>;
|
|
84
|
+
|
|
80
85
|
@inject(OpenerService)
|
|
81
86
|
protected readonly openerService: OpenerService;
|
|
82
87
|
|
|
@@ -116,12 +121,18 @@ export class WalkthroughService implements Disposable {
|
|
|
116
121
|
// Plugins are deployed while the progress is still being read. Registering a walkthrough before
|
|
117
122
|
// that would report its completed steps as pending, so every sync waits for the progress.
|
|
118
123
|
this.progressReady = this.loadProgress().then(() => {
|
|
119
|
-
this.
|
|
124
|
+
this.syncWalkthroughs();
|
|
120
125
|
this.establishPluginBaseline();
|
|
121
126
|
});
|
|
122
127
|
|
|
123
128
|
this.toDispose.push(this.pluginSupport.onDidChangePlugins(() => this.handlePluginsChanged()));
|
|
124
129
|
|
|
130
|
+
for (const provider of this.walkthroughProviders.getContributions()) {
|
|
131
|
+
if (provider.onDidChange) {
|
|
132
|
+
this.toDispose.push(provider.onDidChange(() => this.handleProvidersChanged()));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
125
136
|
this.toDispose.push(this.commandRegistry.onDidExecuteCommand(e => {
|
|
126
137
|
this.handleCompletionEvent(`onCommand:${e.commandId}`);
|
|
127
138
|
}));
|
|
@@ -142,11 +153,16 @@ export class WalkthroughService implements Disposable {
|
|
|
142
153
|
}));
|
|
143
154
|
}
|
|
144
155
|
|
|
156
|
+
protected async handleProvidersChanged(): Promise<void> {
|
|
157
|
+
await this.progressReady;
|
|
158
|
+
this.syncWalkthroughs();
|
|
159
|
+
}
|
|
160
|
+
|
|
145
161
|
protected async handlePluginsChanged(): Promise<void> {
|
|
146
162
|
await this.progressReady;
|
|
147
163
|
const previousIds = this.knownPluginIds;
|
|
148
164
|
const baselineEstablished = this.pluginBaselineEstablished;
|
|
149
|
-
this.
|
|
165
|
+
this.syncWalkthroughs();
|
|
150
166
|
this.establishPluginBaseline();
|
|
151
167
|
if (!baselineEstablished) {
|
|
152
168
|
// The plugins deployed while the application was starting are not new installations.
|
|
@@ -262,9 +278,39 @@ export class WalkthroughService implements Disposable {
|
|
|
262
278
|
await this.storageService.setData(WALKTHROUGH_PROGRESS_KEY, this.progressState);
|
|
263
279
|
}
|
|
264
280
|
|
|
265
|
-
|
|
266
|
-
|
|
281
|
+
/**
|
|
282
|
+
* Register the walkthroughs of both sources - plugins and {@link WalkthroughProvider} contributions - and
|
|
283
|
+
* drop the ones that are gone.
|
|
284
|
+
*
|
|
285
|
+
* Both sources are collected before anything is dropped, so that they cannot delete each other's walkthroughs.
|
|
286
|
+
*/
|
|
287
|
+
protected syncWalkthroughs(): void {
|
|
267
288
|
const seenIds = new Set<string>();
|
|
289
|
+
this.collectPluginWalkthroughs(seenIds);
|
|
290
|
+
this.collectProvidedWalkthroughs(seenIds);
|
|
291
|
+
this.removeUnseenWalkthroughs(seenIds);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
protected collectProvidedWalkthroughs(seenIds: Set<string>): void {
|
|
295
|
+
for (const provider of this.walkthroughProviders.getContributions()) {
|
|
296
|
+
try {
|
|
297
|
+
for (const definition of provider.getWalkthroughs()) {
|
|
298
|
+
if (seenIds.has(definition.id)) {
|
|
299
|
+
this.logger.warn(`The walkthrough '${definition.id}' is contributed more than once; only the first contribution is used.`);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
seenIds.add(definition.id);
|
|
303
|
+
this.registerIfChanged(definition.id, definition, () => this.registerWalkthrough(definition.id, definition));
|
|
304
|
+
}
|
|
305
|
+
} catch (error) {
|
|
306
|
+
// A failing provider must not stop the other walkthroughs, nor reject the sync it runs in.
|
|
307
|
+
this.logger.error('Could not collect the walkthroughs of a provider.', error);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
protected collectPluginWalkthroughs(seenIds: Set<string>): void {
|
|
313
|
+
const plugins = this.pluginSupport.plugins;
|
|
268
314
|
|
|
269
315
|
for (const pluginMeta of plugins) {
|
|
270
316
|
if (pluginMeta.outOfSync) {
|
|
@@ -284,15 +330,27 @@ export class WalkthroughService implements Disposable {
|
|
|
284
330
|
for (const contribution of deployed.contributes.walkthroughs) {
|
|
285
331
|
const fullId = `${contribution.pluginId}.${contribution.id}`;
|
|
286
332
|
seenIds.add(fullId);
|
|
287
|
-
|
|
288
|
-
const signature = JSON.stringify(contribution);
|
|
289
|
-
if (this.contributionSignatures.get(fullId) !== signature) {
|
|
290
|
-
this.contributionSignatures.set(fullId, signature);
|
|
291
|
-
this.registerWalkthrough(contribution);
|
|
292
|
-
}
|
|
333
|
+
this.registerIfChanged(fullId, contribution, () => this.registerWalkthrough(fullId, contribution, contribution.pluginId, contribution.pluginIcon));
|
|
293
334
|
}
|
|
294
335
|
}
|
|
336
|
+
}
|
|
295
337
|
|
|
338
|
+
/**
|
|
339
|
+
* Register a walkthrough anew whenever its definition changed, for instance because the contributing plugin
|
|
340
|
+
* was updated. An unchanged definition keeps the walkthrough as it is, progress included.
|
|
341
|
+
*/
|
|
342
|
+
protected registerIfChanged(id: string, definition: WalkthroughDefinition, register: () => void): void {
|
|
343
|
+
const signature = JSON.stringify(definition);
|
|
344
|
+
if (this.contributionSignatures.get(id) !== signature) {
|
|
345
|
+
this.contributionSignatures.set(id, signature);
|
|
346
|
+
register();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Drop every walkthrough that none of the sources contributed any more.
|
|
352
|
+
*/
|
|
353
|
+
protected removeUnseenWalkthroughs(seenIds: Set<string>): void {
|
|
296
354
|
let changed = false;
|
|
297
355
|
for (const id of this.walkthroughs.keys()) {
|
|
298
356
|
if (!seenIds.has(id)) {
|
|
@@ -311,24 +369,23 @@ export class WalkthroughService implements Disposable {
|
|
|
311
369
|
}
|
|
312
370
|
}
|
|
313
371
|
|
|
314
|
-
protected registerWalkthrough(
|
|
315
|
-
const
|
|
316
|
-
const completedSteps = this.progressState.completedSteps[fullId] || [];
|
|
372
|
+
protected registerWalkthrough(id: string, definition: WalkthroughDefinition, pluginId?: string, pluginIcon?: string): void {
|
|
373
|
+
const completedSteps = this.progressState.completedSteps[id] || [];
|
|
317
374
|
|
|
318
|
-
const steps: WalkthroughStep[] =
|
|
375
|
+
const steps: WalkthroughStep[] = definition.steps.map(step => this.toWalkthroughStep(step, completedSteps));
|
|
319
376
|
|
|
320
377
|
const walkthrough: Walkthrough = {
|
|
321
|
-
id
|
|
322
|
-
title:
|
|
323
|
-
description:
|
|
378
|
+
id,
|
|
379
|
+
title: definition.title,
|
|
380
|
+
description: definition.description,
|
|
324
381
|
steps,
|
|
325
|
-
when:
|
|
326
|
-
icon:
|
|
327
|
-
pluginId
|
|
328
|
-
pluginIcon
|
|
382
|
+
when: definition.when,
|
|
383
|
+
icon: definition.icon,
|
|
384
|
+
pluginId,
|
|
385
|
+
pluginIcon
|
|
329
386
|
};
|
|
330
387
|
|
|
331
|
-
this.walkthroughs.set(
|
|
388
|
+
this.walkthroughs.set(id, walkthrough);
|
|
332
389
|
this.contextKeys = undefined;
|
|
333
390
|
// A context key that is already set has to complete its steps right away; without this, a step keyed on a
|
|
334
391
|
// static context - `onContext:isLinux` for instance - would wait for a change that never comes.
|
|
@@ -336,7 +393,7 @@ export class WalkthroughService implements Disposable {
|
|
|
336
393
|
this.onDidChangeWalkthroughsEmitter.fire();
|
|
337
394
|
}
|
|
338
395
|
|
|
339
|
-
protected toWalkthroughStep(step:
|
|
396
|
+
protected toWalkthroughStep(step: WalkthroughStepDefinition, completedSteps: string[]): WalkthroughStep {
|
|
340
397
|
return {
|
|
341
398
|
id: step.id,
|
|
342
399
|
title: step.title,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { Event } from '@theia/core/lib/common/event';
|
|
18
|
+
import { WalkthroughDefinition } from './walkthrough-types';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Contribution point for walkthroughs provided by a Theia extension, next to the walkthroughs that plugins
|
|
22
|
+
* contribute through their `contributes.walkthroughs` manifest section.
|
|
23
|
+
*
|
|
24
|
+
* Both sources are treated alike: contributed walkthroughs are listed on the welcome page, keep their progress,
|
|
25
|
+
* and can be opened with the `walkthrough.open` command.
|
|
26
|
+
*/
|
|
27
|
+
export const WalkthroughProvider = Symbol('WalkthroughProvider');
|
|
28
|
+
export interface WalkthroughProvider {
|
|
29
|
+
/**
|
|
30
|
+
* The walkthroughs of this provider. Called again whenever {@link onDidChange} fires.
|
|
31
|
+
*
|
|
32
|
+
* The ids are used as they are, so they have to be unique across the application. A walkthrough contributed
|
|
33
|
+
* by a plugin is identified by `<pluginId>.<walkthroughId>`, which a provider must not imitate.
|
|
34
|
+
*/
|
|
35
|
+
getWalkthroughs(): WalkthroughDefinition[];
|
|
36
|
+
/** Fired when the result of {@link getWalkthroughs} changed. */
|
|
37
|
+
readonly onDidChange?: Event<void>;
|
|
38
|
+
}
|
|
@@ -16,24 +16,39 @@
|
|
|
16
16
|
|
|
17
17
|
import { WalkthroughStepMedia } from '@theia/plugin-ext/lib/common/plugin-protocol';
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* A step as it is contributed, before the service tracks its completion.
|
|
21
|
+
*/
|
|
22
|
+
export interface WalkthroughStepDefinition {
|
|
20
23
|
id: string;
|
|
21
24
|
title: string;
|
|
22
25
|
description: string;
|
|
23
26
|
media?: WalkthroughStepMedia;
|
|
24
27
|
completionEvents?: string[];
|
|
25
28
|
when?: string;
|
|
26
|
-
isComplete: boolean;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
/**
|
|
32
|
+
* A walkthrough as it is contributed, either by a plugin or by a `WalkthroughProvider`.
|
|
33
|
+
*/
|
|
34
|
+
export interface WalkthroughDefinition {
|
|
30
35
|
id: string;
|
|
31
36
|
title: string;
|
|
32
37
|
description: string;
|
|
33
|
-
steps:
|
|
38
|
+
steps: WalkthroughStepDefinition[];
|
|
34
39
|
when?: string;
|
|
40
|
+
/** Name of the codicon shown on the card and in the detail header, without the `codicon-` prefix. */
|
|
35
41
|
icon?: string;
|
|
36
|
-
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface WalkthroughStep extends WalkthroughStepDefinition {
|
|
45
|
+
isComplete: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface Walkthrough extends WalkthroughDefinition {
|
|
49
|
+
steps: WalkthroughStep[];
|
|
50
|
+
/** Id of the contributing plugin, or `undefined` for a walkthrough contributed by a Theia extension. */
|
|
51
|
+
pluginId?: string;
|
|
37
52
|
/** Icon of the contributing extension, as a backend relative path. */
|
|
38
53
|
pluginIcon?: string;
|
|
39
54
|
}
|