parent
e8e6d64105
commit
f6f5111700
|
@ -113,22 +113,23 @@ export interface IAuthenticationContribution {
|
|||
readonly label: string;
|
||||
}
|
||||
|
||||
export interface IWelcomeItem {
|
||||
export interface IWalkthroughTask {
|
||||
readonly id: string;
|
||||
readonly title: string;
|
||||
readonly description: string;
|
||||
readonly button: { title: string } & ({ command?: never, link: string } | { command: string, link?: never }),
|
||||
readonly media: { path: string | { hc: string, light: string, dark: string }, altText: string },
|
||||
readonly doneOn?:
|
||||
| { event: string; command?: never }
|
||||
| { event?: never; command: string };
|
||||
readonly button:
|
||||
| { title: string, link: string, command?: never }
|
||||
| { title: string, command: string, link?: never },
|
||||
readonly media: { path: string, altText: string },
|
||||
readonly doneOn?: { command: string };
|
||||
readonly when?: string;
|
||||
}
|
||||
|
||||
export interface IWelcomeCategory {
|
||||
export interface IWalkthrough {
|
||||
readonly id: string,
|
||||
readonly title: string;
|
||||
readonly description: string;
|
||||
readonly tasks: IWalkthroughTask[];
|
||||
readonly when?: string;
|
||||
}
|
||||
|
||||
|
@ -151,8 +152,7 @@ export interface IExtensionContributions {
|
|||
readonly customEditors?: readonly IWebviewEditor[];
|
||||
readonly codeActions?: readonly ICodeActionContribution[];
|
||||
authentication?: IAuthenticationContribution[];
|
||||
welcomeItems?: { [category: string]: IWelcomeItem[] };
|
||||
welcomeCategories?: IWelcomeCategory[];
|
||||
walkthroughs?: IWalkthrough[];
|
||||
}
|
||||
|
||||
export type ExtensionKind = 'ui' | 'workspace' | 'web';
|
||||
|
|
|
@ -10,6 +10,7 @@ import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from
|
|||
import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
@ -145,3 +146,132 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
|||
},
|
||||
}
|
||||
});
|
||||
|
||||
ExtensionsRegistry.registerExtensionPoint({
|
||||
extensionPoint: 'walkthroughs',
|
||||
jsonSchema: {
|
||||
doNotSuggest: true,
|
||||
description: localize('walkthroughs', "Contribute collections of tasks to help users with your extension. Experimental, available in VS Code Insiders only."),
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['id', 'title', 'description', 'tasks'],
|
||||
defaultSnippets: [{ body: { 'id': '$1', 'title': '$2', 'description': '$3', 'tasks': [] } }],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.id', "Unique identifier for this walkthrough."),
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.title', "Title of walkthrough.")
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.description', "Description of walkthrough.")
|
||||
},
|
||||
when: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.when', "Context key expression to control the visibility of this walkthrough.")
|
||||
},
|
||||
tasks: {
|
||||
type: 'array',
|
||||
description: localize('walkthroughs.tasks', "Tasks to complete as part of this walkthrough."),
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['id', 'title', 'description', 'button', 'media'],
|
||||
defaultSnippets: [{
|
||||
body: {
|
||||
'id': '$1', 'title': '$2', 'description': '$3',
|
||||
'button': { 'title': '$4', 'command': '$5' },
|
||||
'doneOn': { 'command': '$5' },
|
||||
'media': { 'path': '$6', 'altText': '$7' }
|
||||
}
|
||||
}],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.tasks.id', "Unique identifier for this task. This is used to keep track of which tasks have been completed."),
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.tasks.title', "Title of task.")
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.tasks.description', "Description of task.")
|
||||
},
|
||||
button: {
|
||||
description: localize('walkthroughs.tasks.button', "The task's button, which can either link to an external resource or run a command"),
|
||||
oneOf: [
|
||||
{
|
||||
type: 'object',
|
||||
required: ['title', 'command'],
|
||||
defaultSnippets: [{ 'body': { 'title': '$1', 'command': '$2' } }],
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.tasks.button.title', "Title of button.")
|
||||
},
|
||||
command: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.tasks.button.command', "Command to run when button is clicked.")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
required: ['title', 'link'],
|
||||
defaultSnippets: [{ 'body': { 'title': '$1', 'link': '$2' } }],
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.tasks.button.title', "Title of button.")
|
||||
},
|
||||
link: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.tasks.button.link', "Link to open when button is clicked. Opening this link will mark the task completed.")
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
media: {
|
||||
type: 'object',
|
||||
required: ['path', 'altText'],
|
||||
description: localize('walkthroughs.tasks.media', "Image to show alongside this task."),
|
||||
defaultSnippets: [{ 'body': { 'altText': '$1', 'path': '$2' } }],
|
||||
properties: {
|
||||
path: {
|
||||
description: localize('walkthroughs.tasks.media.path', "Path to an image, relative to extension directory."),
|
||||
type: 'string',
|
||||
},
|
||||
altText: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.tasks.media.altText', "Alternate text to display when the image cannot be loaded or in screen readers.")
|
||||
}
|
||||
}
|
||||
},
|
||||
doneOn: {
|
||||
description: localize('walkthroughs.tasks.doneOn', "Signal to mark task as complete."),
|
||||
type: 'object',
|
||||
required: ['command'],
|
||||
defaultSnippets: [{ 'body': { command: '$1' } }],
|
||||
properties: {
|
||||
'command': {
|
||||
description: localize('walkthroughs.tasks.oneOn.command', "Mark task done when the specified command is executed."),
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
},
|
||||
when: {
|
||||
type: 'string',
|
||||
description: localize('walkthroughs.tasks.when', "Context key expression to control the visibility of this task.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,11 +7,9 @@ import { Emitter, Event } from 'vs/base/common/event';
|
|||
import { FileAccess } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { content } from 'vs/workbench/services/gettingStarted/common/gettingStartedContent';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export const enum GettingStartedCategory {
|
||||
Beginner = 'Beginner',
|
||||
|
@ -172,161 +170,3 @@ content.forEach(category => {
|
|||
|
||||
Registry.add(GettingStartedRegistryID, registryImpl);
|
||||
export const GettingStartedRegistry: IGettingStartedRegistry = Registry.as(GettingStartedRegistryID);
|
||||
|
||||
ExtensionsRegistry.registerExtensionPoint({
|
||||
extensionPoint: 'welcomeItems',
|
||||
jsonSchema: {
|
||||
doNotSuggest: true,
|
||||
description: localize('gettingStarted', "Contribute items to help users in getting started with your extension. Keys correspond to categories contributed via welcomeCategories contribution point. Experimental, available in VS Code Insiders only."),
|
||||
type: 'object',
|
||||
additionalProperties: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['id', 'title', 'description', 'button', 'media'],
|
||||
defaultSnippets: [{ body: { 'id': '$1', 'title': '$2', 'description': '$3', 'button': { 'title': '$4' }, 'media': { 'path': '$5', 'altText': '$6' } } }],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: localize('gettingStarted.id', "Unique identifier for this item."),
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: localize('gettingStarted.title', "Title of item.")
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: localize('gettingStarted.description', "Description of item.")
|
||||
},
|
||||
button: {
|
||||
description: localize('gettingStarted.button', "The item's button, which can either link to an external resource or run a command"),
|
||||
oneOf: [
|
||||
{
|
||||
type: 'object',
|
||||
required: ['title', 'command'],
|
||||
defaultSnippets: [{ 'body': { 'title': '$1', 'command': '$2' } }],
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
description: localize('gettingStarted.button.title', "Title of button.")
|
||||
},
|
||||
command: {
|
||||
type: 'string',
|
||||
description: localize('gettingStarted.button.command', "Command to run when button is clicked. Running this command will mark the item completed.")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
required: ['title', 'link'],
|
||||
defaultSnippets: [{ 'body': { 'title': '$1', 'link': '$2' } }],
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
description: localize('gettingStarted.button.title', "Title of button.")
|
||||
},
|
||||
link: {
|
||||
type: 'string',
|
||||
description: localize('gettingStarted.button.link', "Link to open when button is clicked. Opening this link will mark the item completed.")
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
media: {
|
||||
type: 'object',
|
||||
required: ['path', 'altText'],
|
||||
description: localize('gettingStarted.media', "Image to show alongside this item."),
|
||||
defaultSnippets: [{ 'body': { 'altText': '$1' } }],
|
||||
properties: {
|
||||
path: {
|
||||
description: localize('gettingStarted.media.path', "Either a single string path to an image to be used on all color themes, or separate paths for light, dark, and high contrast themes."),
|
||||
oneOf: [
|
||||
{
|
||||
type: 'string',
|
||||
defaultSnippets: [{ 'body': '$1' }],
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
defaultSnippets: [{ 'body': { 'hc': '$1', 'light': '$2', 'dark': '$3' } }],
|
||||
required: ['hc', 'light', 'dark'],
|
||||
properties: {
|
||||
hc: { type: 'string' },
|
||||
light: { type: 'string' },
|
||||
dark: { type: 'string' },
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
altText: {
|
||||
type: 'string',
|
||||
description: localize('gettingStarted.media.altText', "Alternate text to display when the image cannot be loaded or in screen readers.")
|
||||
}
|
||||
}
|
||||
},
|
||||
doneOn: {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'object',
|
||||
required: ['event'],
|
||||
properties: {
|
||||
'event': {
|
||||
description: localize('gettingStarted.oneOn.event', "Mark item done when the specified event is marked via the invoking the `welcomeItems.markEvent` command."),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
required: ['command'],
|
||||
properties: {
|
||||
'command': {
|
||||
description: localize('gettingStarted.oneOn.command', "Mark item done when the specified command is executed."),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
description: localize('gettingStarted.doneOn', "Signal to mark item as complete. If not defined, running the button's command will mark the item complete.")
|
||||
},
|
||||
when: {
|
||||
type: 'string',
|
||||
description: localize('gettingStarted.when', "Context key expression to control the visibility of this getting started item.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ExtensionsRegistry.registerExtensionPoint({
|
||||
extensionPoint: 'welcomeCategories',
|
||||
jsonSchema: {
|
||||
doNotSuggest: true,
|
||||
description: localize('welcomeCategories', "Contribute categories of items to help users in getting started with your extension. Items themselves are contributed via welcomeItems contribution point. Experimental, available in VS Code Insiders only."),
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['id', 'title', 'description'],
|
||||
defaultSnippets: [{ body: { 'id': '$1', 'title': '$2', 'description': '$3' } }],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: localize('welcomeCategories.id', "Unique identifier for this category."),
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: localize('welcomeCategories.title', "Title of category.")
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: localize('welcomeCategories.description', "Description of category.")
|
||||
},
|
||||
when: {
|
||||
type: 'string',
|
||||
description: localize('welcomeCategories.when', "Context key expression to control the visibility of this category.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -139,20 +139,17 @@ export class GettingStartedService extends Disposable implements IGettingStarted
|
|||
if (!this.trackedExtensions.has(ExtensionIdentifier.toKey(extension.identifier))) {
|
||||
this.trackedExtensions.add(ExtensionIdentifier.toKey(extension.identifier));
|
||||
|
||||
if ((extension.contributes?.welcomeCategories || extension.contributes?.welcomeItems) && this.productService.quality === 'stable') {
|
||||
if ((extension.contributes?.walkthroughs?.length) && this.productService.quality === 'stable') {
|
||||
console.warn('Extension', extension.identifier.value, 'contributes welcome page content but this is a Stable build and extension contributions are only available in Insiders. The contributed content will be disregarded.');
|
||||
return;
|
||||
}
|
||||
|
||||
const contributedCategories = new Map();
|
||||
|
||||
extension.contributes?.welcomeCategories?.forEach(category => {
|
||||
const categoryID = extension.identifier.value + '.' + category.id;
|
||||
contributedCategories.set(category.id, categoryID);
|
||||
extension.contributes?.walkthroughs?.forEach(section => {
|
||||
const categoryID = extension.identifier.value + '#' + section.id;
|
||||
this.registry.registerCategory({
|
||||
content: { type: 'items' },
|
||||
description: category.description,
|
||||
title: category.title,
|
||||
description: section.description,
|
||||
title: section.title,
|
||||
id: categoryID,
|
||||
icon: {
|
||||
type: 'image',
|
||||
|
@ -160,31 +157,30 @@ export class GettingStartedService extends Disposable implements IGettingStarted
|
|||
? FileAccess.asBrowserUri(joinPath(extension.extensionLocation, extension.icon)).toString(true)
|
||||
: DefaultIconPath
|
||||
},
|
||||
when: ContextKeyExpr.deserialize(category.when) ?? ContextKeyExpr.true(),
|
||||
when: ContextKeyExpr.deserialize(section.when) ?? ContextKeyExpr.true(),
|
||||
});
|
||||
});
|
||||
try {
|
||||
|
||||
Object.entries(extension.contributes?.welcomeItems ?? {}).forEach(([category, items]) =>
|
||||
items.forEach((item, index) =>
|
||||
this.registry.registerTask({
|
||||
button: item.button,
|
||||
description: item.description,
|
||||
media: { type: 'image', altText: item.media.altText, path: convertPaths(item.media.path) },
|
||||
doneOn: item.doneOn?.event
|
||||
? { eventFired: item.doneOn.event }
|
||||
: item.doneOn?.command ?
|
||||
{ commandExecuted: item.doneOn.command }
|
||||
: item.button.command
|
||||
? { commandExecuted: item.button.command }
|
||||
: { eventFired: `linkOpened:${item.button.link}` },
|
||||
id: extension.identifier.value + '.' + item.id,
|
||||
title: item.title,
|
||||
when: ContextKeyExpr.deserialize(item.when) ?? ContextKeyExpr.true(),
|
||||
category: contributedCategories.get(category) ?? category,
|
||||
order: index,
|
||||
})
|
||||
)
|
||||
);
|
||||
section.tasks.forEach((task, index) =>
|
||||
this.registry.registerTask({
|
||||
button: task.button,
|
||||
description: task.description,
|
||||
media: { type: 'image', altText: task.media.altText, path: convertPaths(task.media.path) },
|
||||
doneOn: task.doneOn?.command
|
||||
? { commandExecuted: task.doneOn.command }
|
||||
: task.button.command
|
||||
? { commandExecuted: task.button.command }
|
||||
: { eventFired: `linkOpened:${task.button.link}` },
|
||||
id: extension.identifier.value + '#' + task.id,
|
||||
title: task.title,
|
||||
when: ContextKeyExpr.deserialize(task.when) ?? ContextKeyExpr.true(),
|
||||
category: categoryID,
|
||||
order: index,
|
||||
}));
|
||||
} catch (e) {
|
||||
console.error('Error registering walkthrough tasks for ', categoryID, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue