From 144d6066ef385fb0ce1ab7faf626410158c24f31 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 29 Mar 2020 19:23:46 +0200 Subject: [PATCH] #89962 Enhance extension pack representation - add count badge in extension viewlet - show extensions in details page --- .../extensions/browser/extensionEditor.ts | 65 +++-- .../extensions/browser/extensionsActions.ts | 16 +- .../extensions/browser/extensionsList.ts | 7 +- .../extensions/browser/extensionsViewer.ts | 110 +++++++-- .../extensions/browser/extensionsWidgets.ts | 36 ++- .../extensions/browser/media/extension.css | 204 ++++++++++++++++ .../browser/media/extensionActions.css | 52 ---- .../browser/media/extensionEditor.css | 101 ++++++++ .../browser/media/extensionsViewlet.css | 230 ++---------------- .../browser/media/extensionsWidgets.css | 21 ++ 10 files changed, 515 insertions(+), 327 deletions(-) create mode 100644 src/vs/workbench/contrib/extensions/browser/media/extension.css diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 7bd241744db..2df6f274872 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -40,7 +40,7 @@ import { Color } from 'vs/base/common/color'; import { assign } from 'vs/base/common/objects'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ExtensionsTree, ExtensionData } from 'vs/workbench/contrib/extensions/browser/extensionsViewer'; +import { ExtensionsTree, ExtensionData, ExtensionsGridView, getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer'; import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -131,7 +131,6 @@ const NavbarSection = { Contributions: 'contributions', Changelog: 'changelog', Dependencies: 'dependencies', - ExtensionPack: 'extensionPack' }; interface ILayoutParticipant { @@ -435,9 +434,6 @@ export class ExtensionEditor extends BaseEditor { if (manifest) { combinedInstallAction.manifest = manifest; } - if (extension.extensionPack.length) { - template.navbar.push(NavbarSection.ExtensionPack, localize('extensionPack', "Extension Pack"), localize('extensionsPack', "Set of extensions that can be installed together")); - } if (manifest && manifest.contributes) { template.navbar.push(NavbarSection.Contributions, localize('contributions', "Feature Contributions"), localize('contributionstooltip', "Lists contributions to VS Code by this extension")); } @@ -575,7 +571,6 @@ export class ExtensionEditor extends BaseEditor { case NavbarSection.Contributions: return this.openContributions(template); case NavbarSection.Changelog: return this.openChangelog(template); case NavbarSection.Dependencies: return this.openDependencies(extension, template); - case NavbarSection.ExtensionPack: return this.openExtensionPack(extension, template); } return Promise.resolve(null); } @@ -830,10 +825,37 @@ export class ExtensionEditor extends BaseEditor { `; } - private openReadme(template: IExtensionEditorTemplate): Promise { + private async openReadme(template: IExtensionEditorTemplate): Promise { + const manifest = await this.extensionManifest!.get().promise; + if (manifest && manifest.extensionPack && manifest.extensionPack.length) { + return this.openExtensionPackReadme(manifest, template); + } return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), template); } + private async openExtensionPackReadme(manifest: IExtensionManifest, template: IExtensionEditorTemplate): Promise { + const extensionPackReadme = append(template.content, $('div', { class: 'extension-pack-readme' })); + extensionPackReadme.style.margin = '0 auto'; + extensionPackReadme.style.maxWidth = '882px'; + + const extensionPack = append(extensionPackReadme, $('div', { class: 'extension-pack' })); + toggleClass(extensionPackReadme, 'narrow', manifest.extensionPack!.length <= 2); + + const extensionPackHeader = append(extensionPack, $('div.header')); + extensionPackHeader.textContent = localize('extension pack', "Extension Pack ({0})", manifest.extensionPack!.length); + const extensionPackContent = append(extensionPack, $('div', { class: 'extension-pack-content' })); + extensionPackContent.setAttribute('tabindex', '0'); + append(extensionPack, $('div.footer')); + const readmeContent = append(extensionPackReadme, $('div.readme-content')); + + await Promise.all([ + this.renderExtensionPack(manifest, extensionPackContent), + this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), { ...template, ...{ content: readmeContent } }), + ]); + + return { focus: () => extensionPackContent.focus() }; + } + private openChangelog(template: IExtensionEditorTemplate): Promise { return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template); } @@ -915,28 +937,19 @@ export class ExtensionEditor extends BaseEditor { return Promise.resolve({ focus() { dependenciesTree.domFocus(); } }); } - private openExtensionPack(extension: IExtension, template: IExtensionEditorTemplate): Promise { + private async renderExtensionPack(manifest: IExtensionManifest, parent: HTMLElement): Promise { const content = $('div', { class: 'subcontent' }); - const scrollableContent = new DomScrollableElement(content, {}); - append(template.content, scrollableContent.getDomNode()); - this.contentDisposables.add(scrollableContent); + const scrollableContent = new DomScrollableElement(content, { useShadows: false }); + append(parent, scrollableContent.getDomNode()); - const extensionsPackTree = this.instantiationService.createInstance(ExtensionsTree, - new ExtensionData(extension, null, extension => extension.extensionPack || [], this.extensionsWorkbenchService), content, - { - listBackground: editorBackground - }); - const layout = () => { - scrollableContent.scanDomNode(); - const scrollDimensions = scrollableContent.getScrollDimensions(); - extensionsPackTree.layout(scrollDimensions.height); - }; - const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout }); - this.contentDisposables.add(toDisposable(removeLayoutParticipant)); - - this.contentDisposables.add(extensionsPackTree); + const extensionsGridView = this.instantiationService.createInstance(ExtensionsGridView, content); + const extensions: IExtension[] = await getExtensions(manifest.extensionPack!, this.extensionsWorkbenchService); + extensionsGridView.setExtensions(extensions); scrollableContent.scanDomNode(); - return Promise.resolve({ focus() { extensionsPackTree.domFocus(); } }); + + this.contentDisposables.add(scrollableContent); + this.contentDisposables.add(extensionsGridView); + this.contentDisposables.add(toDisposable(arrays.insert(this.layoutParticipants, { layout: () => scrollableContent.scanDomNode() }))); } private renderSettings(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index c27aac02c4f..b431d5abe38 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -3187,49 +3187,49 @@ export const extensionButtonProminentHoverBackground = registerColor('extensionB registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { const foregroundColor = theme.getColor(foreground); if (foregroundColor) { - collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`); + collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`); } const buttonBackgroundColor = theme.getColor(buttonBackground); if (buttonBackgroundColor) { - collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action { background-color: ${buttonBackgroundColor}; }`); + collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action { background-color: ${buttonBackgroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action { background-color: ${buttonBackgroundColor}; }`); } const buttonForegroundColor = theme.getColor(buttonForeground); if (buttonForegroundColor) { - collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action { color: ${buttonForegroundColor}; }`); + collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action { color: ${buttonForegroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action { color: ${buttonForegroundColor}; }`); } const buttonHoverBackgroundColor = theme.getColor(buttonHoverBackground); if (buttonHoverBackgroundColor) { - collector.addRule(`.extension .monaco-action-bar .action-item:hover .action-label.extension-action { background-color: ${buttonHoverBackgroundColor}; }`); + collector.addRule(`.extension-list-item .monaco-action-bar .action-item:hover .action-label.extension-action { background-color: ${buttonHoverBackgroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item:hover .action-label.extension-action { background-color: ${buttonHoverBackgroundColor}; }`); } const contrastBorderColor = theme.getColor(contrastBorder); if (contrastBorderColor) { - collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action { border: 1px solid ${contrastBorderColor}; }`); + collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action { border: 1px solid ${contrastBorderColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action { border: 1px solid ${contrastBorderColor}; }`); } const extensionButtonProminentBackgroundColor = theme.getColor(extensionButtonProminentBackground); if (extensionButtonProminentBackground) { - collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); + collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); } const extensionButtonProminentForegroundColor = theme.getColor(extensionButtonProminentForeground); if (extensionButtonProminentForeground) { - collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); + collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); } const extensionButtonProminentHoverBackgroundColor = theme.getColor(extensionButtonProminentHoverBackground); if (extensionButtonProminentHoverBackground) { - collector.addRule(`.extension .monaco-action-bar .action-item:hover .action-label.extension-action.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`); + collector.addRule(`.extension-list-item .monaco-action-bar .action-item:hover .action-label.extension-action.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item:hover .action-label.extension-action.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`); } }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 110810654c9..4b0716b5750 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/extension'; import { localize } from 'vs/nls'; import { append, $, addClass, removeClass, toggleClass } from 'vs/base/browser/dom'; import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; @@ -16,7 +17,7 @@ import { domEvent } from 'vs/base/browser/event'; import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionViewItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, ExtensionToolTipAction, LocalInstallAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; +import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -68,10 +69,11 @@ export class Renderer implements IPagedRenderer { renderTemplate(root: HTMLElement): ITemplateData { const recommendationWidget = this.instantiationService.createInstance(RecommendationWidget, root); - const element = append(root, $('.extension')); + const element = append(root, $('.extension-list-item')); const iconContainer = append(element, $('.icon-container')); const icon = append(iconContainer, $('img.icon')); const iconRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, iconContainer, false); + const extensionPackBadgeWidget = this.instantiationService.createInstance(ExtensionPackBadgeWidget, iconContainer); const details = append(element, $('.details')); const headerContainer = append(details, $('.header-container')); const header = append(headerContainer, $('.header')); @@ -115,6 +117,7 @@ export class Renderer implements IPagedRenderer { const widgets = [ recommendationWidget, iconRemoteBadgeWidget, + extensionPackBadgeWidget, headerRemoteBadgeWidget, tooltipWidget, this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts index bf8158fa6e9..9829fadacc0 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { Action } from 'vs/base/common/actions'; import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; import { Event } from 'vs/base/common/event'; @@ -14,7 +14,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IListService, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IAsyncDataSource, ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; @@ -22,6 +22,52 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { CancellationToken } from 'vs/base/common/cancellation'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IColorMapping } from 'vs/platform/theme/common/styler'; +import { Renderer, Delegate } from 'vs/workbench/contrib/extensions/browser/extensionsList'; +import { listFocusForeground, listFocusBackground } from 'vs/platform/theme/common/colorRegistry'; + +export class ExtensionsGridView extends Disposable { + + readonly element: HTMLElement; + private readonly renderer: Renderer; + private readonly delegate: Delegate; + private readonly disposableStore: DisposableStore; + + constructor( + parent: HTMLElement, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super(); + this.element = dom.append(parent, dom.$('.extensions-grid-view')); + this.renderer = this.instantiationService.createInstance(Renderer, { onFocus: Event.None, onBlur: Event.None }); + this.delegate = new Delegate(); + this.disposableStore = new DisposableStore(); + } + + setExtensions(extensions: IExtension[]): void { + this.disposableStore.clear(); + extensions.forEach((e, index) => this.renderExtension(e, index)); + } + + private renderExtension(extension: IExtension, index: number): void { + const extensionContainer = dom.append(this.element, dom.$('.extension-container')); + extensionContainer.style.height = `${this.delegate.getHeight()}px`; + extensionContainer.style.width = `350px`; + extensionContainer.setAttribute('tabindex', '0'); + + const template = this.renderer.renderTemplate(extensionContainer); + this.disposableStore.add(toDisposable(() => this.renderer.disposeTemplate(template))); + + const openExtensionAction = this.instantiationService.createInstance(OpenExtensionAction); + openExtensionAction.extension = extension; + this.disposableStore.add(dom.addDisposableListener(template.name, 'click', (e: MouseEvent) => { + openExtensionAction.run(e.ctrlKey || e.metaKey); + e.stopPropagation(); + e.preventDefault(); + })); + + this.renderer.renderElement(extension, index, template); + } +} export interface IExtensionTemplateData { icon: HTMLImageElement; @@ -101,7 +147,7 @@ export class ExtensionRenderer implements IListRenderer { - if (this._extensionData) { - return this.extensionsWorkdbenchService.open(this._extensionData.extension, { sideByside }); + if (this._extension) { + return this.extensionsWorkdbenchService.open(this._extension, { sideByside }); } return Promise.resolve(); } @@ -246,24 +292,40 @@ export class ExtensionData implements IExtensionData { async getChildren(): Promise { if (this.hasChildren) { - const localById = this.extensionsWorkbenchService.local.reduce((result, e) => { result.set(e.identifier.id.toLowerCase(), e); return result; }, new Map()); - const result: IExtension[] = []; - const toQuery: string[] = []; - for (const extensionId of this.childrenExtensionIds) { - const id = extensionId.toLowerCase(); - const local = localById.get(id); - if (local) { - result.push(local); - } else { - toQuery.push(id); - } - } - if (toQuery.length) { - const galleryResult = await this.extensionsWorkbenchService.queryGallery({ names: toQuery, pageSize: toQuery.length }, CancellationToken.None); - result.push(...galleryResult.firstPage); - } + const result: IExtension[] = await getExtensions(this.childrenExtensionIds, this.extensionsWorkbenchService); return result.map(extension => new ExtensionData(extension, this, this.getChildrenExtensionIds, this.extensionsWorkbenchService)); } return null; } } + +export async function getExtensions(extensions: string[], extensionsWorkbenchService: IExtensionsWorkbenchService): Promise { + const localById = extensionsWorkbenchService.local.reduce((result, e) => { result.set(e.identifier.id.toLowerCase(), e); return result; }, new Map()); + const result: IExtension[] = []; + const toQuery: string[] = []; + for (const extensionId of extensions) { + const id = extensionId.toLowerCase(); + const local = localById.get(id); + if (local) { + result.push(local); + } else { + toQuery.push(id); + } + } + if (toQuery.length) { + const galleryResult = await extensionsWorkbenchService.queryGallery({ names: toQuery, pageSize: toQuery.length }, CancellationToken.None); + result.push(...galleryResult.firstPage); + } + return result; +} + +registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { + const focusBackground = theme.getColor(listFocusBackground); + if (focusBackground) { + collector.addRule(`.extensions-grid-view .extension-container:focus { background-color: ${focusBackground}; outline: none; }`); + } + const focusForeground = theme.getColor(listFocusForeground); + if (focusForeground) { + collector.addRule(`.extensions-grid-view .extension-container:focus { color: ${focusForeground}; }`); + } +}); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index b3064bd12d6..c2957c2c2a3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/extensionsWidgets'; import { Disposable, toDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { IExtension, IExtensionsWorkbenchService, IExtensionContainer } from 'vs/workbench/contrib/extensions/common/extensions'; -import { append, $, addClass } from 'vs/base/browser/dom'; +import { append, $, addClass, removeNode } from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; import { localize } from 'vs/nls'; import { IExtensionTipsService, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -16,6 +16,7 @@ import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeServic import { EXTENSION_BADGE_REMOTE_BACKGROUND, EXTENSION_BADGE_REMOTE_FOREGROUND } from 'vs/workbench/common/theme'; import { Emitter, Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension | null = null; @@ -222,7 +223,7 @@ export class RecommendationWidget extends ExtensionWidget { } const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); if (extRecommendations[this.extension.identifier.id.toLowerCase()]) { - this.element = append(this.parent, $('div.bookmark')); + this.element = append(this.parent, $('div.extension-bookmark')); const recommendation = append(this.element, $('.recommendation')); append(recommendation, $('span.codicon.codicon-star')); const applyBookmarkStyle = (theme: IColorTheme) => { @@ -285,7 +286,7 @@ class RemoteBadge extends Disposable { @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService ) { super(); - this.element = $('div.extension-remote-badge'); + this.element = $('div.extension-badge.extension-remote-badge'); this.render(); } @@ -315,3 +316,32 @@ class RemoteBadge extends Disposable { } } } + +export class ExtensionPackCountWidget extends ExtensionWidget { + + private element: HTMLElement | undefined; + + constructor( + private readonly parent: HTMLElement, + ) { + super(); + this.render(); + this._register(toDisposable(() => this.clear())); + } + + private clear(): void { + if (this.element) { + removeNode(this.element); + } + } + + render(): void { + this.clear(); + if (!this.extension || !this.extension.extensionPack.length) { + return; + } + this.element = append(this.parent, $('.extension-badge.extension-pack-badge')); + const countBadge = new CountBadge(this.element); + countBadge.setCount(this.extension.extensionPack.length); + } +} diff --git a/src/vs/workbench/contrib/extensions/browser/media/extension.css b/src/vs/workbench/contrib/extensions/browser/media/extension.css new file mode 100644 index 00000000000..7f558e187d1 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/media/extension.css @@ -0,0 +1,204 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.extension-list-item { + box-sizing: border-box; + width: 100%; + height: 100%; + padding: 0 0 0 16px; + overflow: hidden; + display: flex; +} + +.extension-list-item > .icon-container { + position: relative; +} + +.extension-list-item > .icon-container > .icon { + width: 42px; + height: 42px; + padding: 10px 14px 10px 0; + flex-shrink: 0; + object-fit: contain; +} + +.extension-list-item > .icon-container .extension-badge { + position: absolute; + bottom: 5px; + width: 22px; + height: 22px; + line-height: 22px; + border-radius: 20px; + display: flex; + align-items: center; + justify-content: center; +} + +.extension-list-item > .icon-container .extension-badge.extension-remote-badge { + right: 5px; +} + +.extension-list-item > .icon-container .extension-remote-badge .codicon { + color: currentColor; +} + +.extension-list-item > .details { + flex: 1; + padding: 4px 0; + overflow: hidden; +} + +.extension-list-item > .details > .header-container { + height: 19px; + display: flex; + overflow: hidden; + padding-right: 11px; +} + +.extension-list-item > .details > .header-container > .header { + display: flex; + align-items: baseline; + flex-wrap: nowrap; + overflow: hidden; + flex: 1; + min-width: 0; +} + +.extension-list-item > .details > .header-container > .header > .name { + font-weight: bold; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.extension-list-item > .details > .header-container > .header > .version { + opacity: 0.85; + font-size: 80%; + padding-left: 6px; + min-width: fit-content; + min-width: -moz-fit-content; +} + +.extension-list-item > .details > .header-container > .header > .version { + flex: 1; +} + +.extension-list-item > .details > .header-container > .header > .install-count, +.extension-list-item > .details > .header-container > .header > .ratings { + display: flex; + align-items: center; +} + +.extension-list-item > .details > .header-container > .header > .install-count:not(:empty) { + font-size: 80%; + margin: 0 6px; +} + +.extension-list-item > .details > .header-container > .header .codicon { + font-size: 120%; + margin-right: 2px; + -webkit-mask: inherit; +} + +.extension-list-item > .details > .header-container > .header > .ratings { + text-align: right; +} + +.extension-list-item > .details > .header-container > .header > .extension-remote-badge-container { + margin-left: 6px; + display: none; +} + +.extension-list-item > .details > .header-container > .header .extension-remote-badge .codicon { + margin-right: 0; +} + +.extension-list-item > .details > .header-container > .header .extension-remote-badge { + width: 14px; + height: 14px; + line-height: 14px; + border-radius: 20px; + text-align: center; + display: flex; + align-items: center; + justify-content: center; +} + +.extension-list-item > .details > .header-container > .header .extension-remote-badge > .codicon { + font-size: 12px; + color: currentColor; +} + +.extension-list-item > .details > .description { + padding-right: 11px; +} + +.extension-list-item > .details > .footer { + display: flex; + justify-content: flex-end; + padding-right: 7px; + height: 24px; + overflow: hidden; +} + +.extension-list-item > .details > .footer > .author { + flex: 1; + font-size: 90%; + opacity: 0.9; + font-weight: 600; +} + +.extension-list-item > .details > .footer > .sync-ignored { + font-size: 11px; +} + +.extension-list-item > .details > .footer > .sync-ignored > .codicon { + font-size: 14px; + vertical-align: text-top; +} + +.extension-list-item > .details > .footer > .monaco-action-bar > .actions-container { + flex-wrap: wrap-reverse; +} + +.extension-list-item > .details > .footer > .monaco-action-bar > .actions-container .extension-action { + max-width: 150px; +} + +.extension-list-item > .details > .footer > .monaco-action-bar .action-label { + margin-top: 0.3em; + margin-left: 0.3em; + line-height: 14px; +} + +.extension-list-item > .details > .footer > .monaco-action-bar .action-label:not(:empty) { + opacity: 0.9; +} + +.extension-list-item > .details >.footer > .monaco-action-bar .action-item .action-label.system-disable.codicon-info, +.extension-list-item > .details >.footer > .monaco-action-bar .action-item .action-label.system-disable.codicon-warning { + margin-top: 0.25em; + margin-left: 0.1em; +} + +.extension-list-item > .details > .footer > .monaco-action-bar .action-item .action-label.extension-action.manage.hide { + display: none; +} + +.extension-list-item > .details > .footer > .monaco-action-bar .action-item .action-label.extension-action.manage { + height: 18px; + width: 10px; + border: none; + color: inherit; + background: none; + outline-offset: 0px; + margin-top: 0.25em; +} + +.extension-list-item .ellipsis { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css index 7bafd26dfbf..9ff1680e003 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css @@ -55,55 +55,3 @@ background-color: transparent; font-style: italic; } - -.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .action-label.disable-status { - margin-left: 0; - margin-top: 6px; - padding-left: 0; -} - -.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .action-label.system-disable { - margin-right: 0.15em; -} - -.extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.system-disable.codicon-info, -.extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.system-disable.codicon-warning { - margin-top: 0.25em; - margin-left: 0.1em; -} - -.monaco-action-bar .action-item .action-label.system-disable.codicon { - opacity: 1; - height: 18px; - width: 10px; -} - -.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.extension-status-label, -.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.disable-status, -.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status { - font-weight: normal; -} - -.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.extension-status-label:hover, -.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.disable-status:hover, -.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status:hover { - opacity: 0.9; -} - -.extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.extension-action.manage.hide { - display: none; -} - -.extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.extension-action.manage { - height: 18px; - width: 10px; - border: none; - color: inherit; - background: none; - outline-offset: 0px; - margin-top: 0.25em; -} - -.extension-editor > .header.recommended > .details > .recommendation > .monaco-action-bar .actions-container { - justify-content: flex-start; -} diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index bfa10d1e062..2853adfa3d8 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -183,6 +183,7 @@ margin-top: 5px; margin-right: 4px; } + .extension-editor > .header > .details > .subtext-container > .monaco-action-bar .action-label { margin-top: 4px; margin-left: 4px; @@ -190,6 +191,38 @@ padding-bottom: 2px; } +.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .action-label.disable-status { + margin-left: 0; + margin-top: 6px; + padding-left: 0; +} + +.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .action-label.system-disable { + margin-right: 0.15em; +} + +.monaco-action-bar .action-item .action-label.system-disable.codicon { + opacity: 1; + height: 18px; + width: 10px; +} + +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.extension-status-label, +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.disable-status, +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status { + font-weight: normal; +} + +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.extension-status-label:hover, +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.disable-status:hover, +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status:hover { + opacity: 0.9; +} + +.extension-editor > .header.recommended > .details > .recommendation > .monaco-action-bar .actions-container { + justify-content: flex-start; +} + .extension-editor > .body { flex: 1; overflow: hidden; @@ -247,6 +280,52 @@ margin-left: 20px; } +.extension-editor > .body > .content > .extension-pack-readme { + height: 100%; +} + +.extension-editor > .body > .content > .extension-pack-readme > .extension-pack { + height: 224px; + padding-left: 20px; +} + +.extension-editor > .body > .content > .extension-pack-readme.narrow > .extension-pack { + height: 142px; +} + +.extension-editor > .body > .content > .extension-pack-readme > .readme-content { + height: calc(100% - 224px); +} + +.extension-editor > .body > .content > .extension-pack-readme.narrow > .readme-content { + height: calc(100% - 142px); +} + +.extension-editor > .body > .content > .extension-pack-readme > .extension-pack > .header, +.extension-editor > .body > .content > .extension-pack-readme > .extension-pack > .footer { + margin-bottom: 10px; + margin-right: 30px; + font-weight: bold; + font-size: 120%; + border-bottom: 1px solid rgba(128, 128, 128, 0.22); + padding: 4px 6px; + line-height: 22px; +} + +.extension-editor > .body > .content > .extension-pack-readme > .extension-pack > .extension-pack-content { + height: calc(100% - 60px); +} + +.extension-editor > .body > .content > .extension-pack-readme > .extension-pack > .extension-pack-content > .monaco-scrollable-element { + height: 100%; +} + +.extension-editor > .body > .content .extension-pack-readme > .extension-pack > .extension-pack-content > .monaco-scrollable-element > .subcontent { + height: 100%; + overflow-y: scroll; + box-sizing: border-box; +} + .extension-editor > .body > .content > .monaco-scrollable-element > .subcontent { height: 100%; padding: 20px; @@ -398,3 +477,25 @@ font-weight: 600; opacity: 0.6; } + +.extension-editor .extensions-grid-view { + display: flex; + flex-wrap: wrap; +} + +.extension-editor .extensions-grid-view > .extension-container { + margin: 0 10px 20px 0; +} + +.extension-editor .extensions-grid-view .extension-list-item { + cursor: default; +} + +.extension-editor .extensions-grid-view .extension-list-item > .details .header > .name { + cursor: pointer; +} + +.extension-editor .extensions-grid-view > .extension-container:focus > .extension-list-item > .details .header > .name, +.extension-editor .extensions-grid-view > .extension-container:focus > .extension-list-item > .details .header > .name:hover { + text-decoration: underline; +} diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css b/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css index c86d44c5b1d..44db7a50ae8 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css @@ -74,241 +74,47 @@ flex-shrink: 0; } -.extensions-viewlet > .extensions .monaco-list-row > .bookmark { - display: inline-block; - height: 20px; - width: 20px; -} - -.extensions-viewlet > .extensions .monaco-list-row > .bookmark > .recommendation { - border-right: 20px solid transparent; - border-top: 20px solid; - box-sizing: border-box; -} - -.extensions-viewlet > .extensions .monaco-list-row > .bookmark > .recommendation > .codicon { +.extensions-viewlet > .extensions .extension-list-item { position: absolute; - top: 1px; - left: 1px; - color: inherit; - font-size: 80%; } -.extensions-viewlet > .extensions .extension { - box-sizing: border-box; - width: 100%; - height: 100%; - padding: 0 0 0 16px; - overflow: hidden; - display: flex; - position: absolute; - top: 0; -} - -.extensions-viewlet > .extensions .extension.loading { +.extensions-viewlet > .extensions .extension-list-item.loading { background: url('loading.svg') center center no-repeat; } -.extensions-viewlet > .extensions .monaco-list-row > .extension > .icon-container { - position: relative; -} - -.extensions-viewlet > .extensions .extension > .icon-container > .icon { - width: 42px; - height: 42px; - padding: 10px 14px 10px 0; - flex-shrink: 0; - object-fit: contain; -} - -.extensions-viewlet > .extensions .monaco-list-row > .extension > .icon-container .extension-remote-badge { - position: absolute; - right: 5px; - bottom: 5px; - width: 22px; - height: 22px; - line-height: 22px; - border-radius: 20px; - display: flex; - align-items: center; - justify-content: center; -} - -.extensions-viewlet > .extensions .monaco-list-row > .extension > .icon-container .extension-remote-badge .codicon { - color: currentColor; -} - -.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header > .extension-remote-badge-container { - margin-left: 6px; -} - -.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header .extension-remote-badge { - width: 14px; - height: 14px; - line-height: 14px; - border-radius: 20px; - text-align: center; - display: flex; - align-items: center; - justify-content: center; -} - -.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header .extension-remote-badge > .codicon { - font-size: 12px; - color: currentColor; -} - -.extensions-viewlet.narrow > .extensions .extension > .icon-container, -.extensions-viewlet > .extensions .extension.loading > .icon-container { +.extensions-viewlet.narrow > .extensions .extension-list-item > .icon-container, +.extensions-viewlet > .extensions .extension-list-item.loading > .icon-container { display: none; } -.extensions-viewlet > .extensions .extension > .details { - flex: 1; - padding: 4px 0; - overflow: hidden; -} - -.extensions-viewlet > .extensions .extension > .details > .header-container { - height: 19px; - display: flex; - overflow: hidden; - padding-right: 11px; -} - -.extensions-viewlet > .extensions .extension > .details > .header-container > .header { - display: flex; - align-items: baseline; - flex-wrap: nowrap; - overflow: hidden; - flex: 1; - min-width: 0; -} - -.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .name { - font-weight: bold; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; -} - -.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .version { - opacity: 0.85; - font-size: 80%; - padding-left: 6px; - min-width: fit-content; - min-width: -moz-fit-content; -} - -.extensions-viewlet:not(.narrow) > .extensions .extension > .details > .header-container > .header > .version { - flex: 1; -} - -.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .install-count, -.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .ratings { - display: flex; - align-items: center; -} - -.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .install-count:not(:empty) { - font-size: 80%; - margin: 0 6px; -} - -.extensions-viewlet > .extensions .extension > .details > .header-container > .header .codicon { - font-size: 120%; - margin-right: 2px; - -webkit-mask: inherit; -} - -.extensions-viewlet>.extensions .extension>.details>.header-container>.header .extension-remote-badge .codicon { - margin-right: 0; -} - -.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .ratings { - text-align: right; -} - -.extensions-viewlet:not(.narrow) > .extensions .extension > .details > .header-container > .header > .extension-remote-badge-container, -.extensions-viewlet.narrow > .extensions .extension > .details > .header-container > .header > .ratings, -.extensions-viewlet.narrow > .extensions .extension > .details > .header-container > .header > .install-count { +.extensions-viewlet:not(.narrow) > .extensions .extension-list-item > .details > .header-container > .header > .extension-remote-badge-container, +.extensions-viewlet.narrow > .extensions .extension-list-item > .details > .header-container > .header > .ratings, +.extensions-viewlet.narrow > .extensions .extension-list-item > .details > .header-container > .header > .install-count { display: none; } -.extensions-viewlet > .extensions .extension > .details > .description { - padding-right: 11px; -} - -.extensions-viewlet > .extensions .extension > .details > .footer { - display: flex; - justify-content: flex-end; - padding-right: 7px; - height: 24px; - overflow: hidden; -} - -.extensions-viewlet > .extensions .extension > .details > .footer > .author { - flex: 1; - font-size: 90%; - opacity: 0.9; - font-weight: 600; -} - -.extensions-viewlet > .extensions .extension > .details > .footer > .sync-ignored { - font-size: 11px; -} - -.extensions-viewlet > .extensions .extension > .details > .footer > .sync-ignored > .codicon { - font-size: 14px; - vertical-align: text-top; -} - -.extensions-viewlet > .extensions .selected .extension > .details > .footer > .author, -.extensions-viewlet > .extensions .selected.focused .extension > .details > .footer > .author { +.extensions-viewlet > .extensions .selected .extension-list-item > .details > .footer > .author, +.extensions-viewlet > .extensions .selected.focused .extension-list-item > .details > .footer > .author { opacity: 1; } -.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar > .actions-container { - flex-wrap: wrap-reverse; -} - -.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar > .actions-container .extension-action { - max-width: 150px; -} - -.extensions-viewlet.narrow > .extensions .extension > .details > .footer > .monaco-action-bar > .actions-container .extension-action { +.extensions-viewlet.narrow > .extensions .extension-list-item > .details > .footer > .monaco-action-bar > .actions-container .extension-action { max-width: 100px; } -.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar .action-label { - margin-top: 0.3em; - margin-left: 0.3em; - line-height: 14px; -} - -.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar .action-label:not(:empty) { - opacity: 0.9; -} - .vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .bookmark, .vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .bookmark, -.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .icon-container > .icon, -.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .icon-container > .icon, -.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .details > .header-container, -.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .details > .header-container, -.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .details > .description, -.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .details > .description, -.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .details > .footer > .author, -.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .details > .footer > .author { +.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .icon-container > .icon, +.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .icon-container > .icon, +.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .header-container, +.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .header-container, +.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .description, +.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .description, +.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .footer > .author, +.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .footer > .author { opacity: 0.5; } -.extensions-viewlet > .extensions .extension .ellipsis { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - .extensions-badge.progress-badge > .badge-content { background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMiAyIDE0IDE0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDIgMiAxNCAxNCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTkgMTZjLTMuODYgMC03LTMuMTQtNy03czMuMTQtNyA3LTdjMy44NTkgMCA3IDMuMTQxIDcgN3MtMy4xNDEgNy03IDd6bTAtMTIuNmMtMy4wODggMC01LjYgMi41MTMtNS42IDUuNnMyLjUxMiA1LjYgNS42IDUuNiA1LjYtMi41MTIgNS42LTUuNi0yLjUxMi01LjYtNS42LTUuNnptMy44NiA3LjFsLTMuMTYtMS44OTZ2LTMuODA0aC0xLjR2NC41OTZsMy44NCAyLjMwNS43Mi0xLjIwMXoiLz48L3N2Zz4="); background-position: center center; diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css b/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css index 7b0d3cb682e..97beafd49fe 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css @@ -32,3 +32,24 @@ .extension-ratings .codicon-star-empty { opacity: .4; } + +.extension-bookmark { + display: inline-block; + height: 20px; + width: 20px; +} + +.extension-bookmark > .recommendation { + border-right: 20px solid transparent; + border-top: 20px solid; + box-sizing: border-box; + position: relative; +} + +.extension-bookmark > .recommendation > .codicon { + position: absolute; + bottom: 9px; + left: 1px; + color: inherit; + font-size: 80%; +} -- GitLab