diff --git a/packages/server/src/cli.ts b/packages/server/src/cli.ts index 790106285..ef18fd934 100644 --- a/packages/server/src/cli.ts +++ b/packages/server/src/cli.ts @@ -200,7 +200,13 @@ const bold = (text: string | number): string | number => { const webpackConfig = require(path.resolve(__dirname, "..", "..", "web", "webpack.config.js")); const compiler = require("webpack")(webpackConfig); app.use(require("webpack-dev-middleware")(compiler, { - logger, + logger: { + trace: (m: string): void => logger.trace("webpack", field("message", m)), + debug: (m: string): void => logger.debug("webpack", field("message", m)), + info: (m: string): void => logger.info("webpack", field("message", m)), + warn: (m: string): void => logger.warn("webpack", field("message", m)), + error: (m: string): void => logger.error("webpack", field("message", m)), + }, publicPath: webpackConfig.output.publicPath, stats: webpackConfig.stats, })); diff --git a/packages/vscode/src/dialog.scss b/packages/vscode/src/dialog.scss index 356270128..2d08911fe 100644 --- a/packages/vscode/src/dialog.scss +++ b/packages/vscode/src/dialog.scss @@ -2,6 +2,7 @@ --primary: #2A2E37; --border: black; --faded: #a0a1a5; + --disabled: #888; --header-background: #161616; --header-foreground: white; --list-active-selection-background: rgb(0, 120, 160); @@ -101,6 +102,12 @@ background-color: var(--list-active-selection-background); color: var(--list-active-selection-foreground); } + + &.disabled, &.disabled:hover { + background-color: var(--primary); + color: var(--disabled); + cursor: initial; + } } } @@ -134,6 +141,11 @@ color: white; } } + + button[disabled], button[disabled]:hover { + color: var(--disabled); + cursor: initial; + } } } diff --git a/packages/vscode/src/dialog.ts b/packages/vscode/src/dialog.ts index 4601b6d0a..1d480bd65 100644 --- a/packages/vscode/src/dialog.ts +++ b/packages/vscode/src/dialog.ts @@ -16,6 +16,9 @@ import { IThemeService } from "vs/platform/theme/common/themeService"; import { workbench } from "./workbench"; import "./dialog.scss"; +/** + * Describes the type of dialog to show. + */ export enum DialogType { NewFolder, Save, @@ -68,8 +71,12 @@ interface DialogEntry { readonly isDirectory: boolean; readonly size: number; readonly lastModified: string; + readonly isDisabled?: boolean; } +/** + * Open and save dialogs. + */ class Dialog { private _path: string | undefined; @@ -265,8 +272,7 @@ class Dialog { } if (element.isDirectory) { this.path = element.fullPath; - } else { - // Open + } else if (!(this.options as OpenDialogOptions).properties.openDirectory) { this.selectEmitter.emit(element.fullPath); } }); @@ -282,12 +288,18 @@ class Dialog { }); buttonsNode.appendChild(cancelBtn); const confirmBtn = document.createElement("button"); - confirmBtn.innerText = "Confirm"; + const openFile = (this.options as OpenDialogOptions).properties.openFile; + confirmBtn.innerText = openFile ? "Open" : "Confirm"; confirmBtn.addEventListener("click", () => { - if (this._path) { + if (this._path && !openFile) { this.selectEmitter.emit(this._path); } }); + // Since a single click opens a file, the only time this button can be + // used is on a directory, which is invalid for opening files. + if (openFile) { + confirmBtn.disabled = true; + } buttonsNode.appendChild(confirmBtn); this.root.appendChild(buttonsNode); this.entryList.layout(); @@ -303,6 +315,9 @@ class Dialog { return this.errorEmitter.event; } + /** + * Remove the dialog. + */ public dispose(): void { this.selectEmitter.dispose(); this.errorEmitter.dispose(); @@ -310,6 +325,9 @@ class Dialog { this.background.remove(); } + /** + * Build and insert the path shown at the top of the dialog. + */ private buildPath(): void { while (this.pathNode.lastChild) { this.pathNode.removeChild(this.pathNode.lastChild); @@ -376,6 +394,9 @@ class Dialog { return (this.entryList).typeFilterController.filter._pattern; } + /** + * List the files and return dialog entries. + */ private async list(directory: string): Promise> { const paths = (await util.promisify(fs.readdir)(directory)).sort(); const stats = await Promise.all(paths.map(p => util.promisify(fs.stat)(path.join(directory, p)))); @@ -386,6 +407,8 @@ class Dialog { isDirectory: stat.isDirectory(), lastModified: stat.mtime.toDateString(), size: stat.size, + // If we are opening a directory, show files as disabled. + isDisabled: !stat.isDirectory() && (this.options as OpenDialogOptions).properties.openDirectory, })); } } @@ -397,11 +420,17 @@ interface DialogEntryData { label: HighlightedLabel; } +/** + * Rendering for the different parts of a dialog entry. + */ class DialogEntryRenderer implements ITreeRenderer { public get templateId(): string { return "dialog-entry"; } + /** + * Append and return containers for each part of the dialog entry. + */ public renderTemplate(container: HTMLElement): DialogEntryData { addClass(container, "dialog-entry"); addClass(container, "dialog-grid"); @@ -422,6 +451,9 @@ class DialogEntryRenderer implements ITreeRenderer, _index: number, templateData: DialogEntryData): void { templateData.icon.className = "dialog-entry-icon monaco-icon-label"; const classes = getIconClasses( @@ -444,8 +476,19 @@ class DialogEntryRenderer implements ITreeRenderer { + public async pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise { showOpenDialog({ - ...(_options.dialogOptions || {}), + ...(options.dialogOptions || {}), properties: { openFile: true, openDirectory: true, @@ -66,9 +66,9 @@ export class WindowsService implements IWindowsService { }); } - public async pickFileAndOpen(_options: INativeOpenDialogOptions): Promise { + public async pickFileAndOpen(options: INativeOpenDialogOptions): Promise { showOpenDialog({ - ...(_options.dialogOptions || {}), + ...(options.dialogOptions || {}), properties: { openFile: true, }, @@ -84,9 +84,15 @@ export class WindowsService implements IWindowsService { }); } - public async pickFolderAndOpen(_options: INativeOpenDialogOptions): Promise { + public async pickFolderAndOpen(options: INativeOpenDialogOptions): Promise { + if (!options.dialogOptions) { + options.dialogOptions = {}; + } + if (!options.dialogOptions.title) { + options.dialogOptions.title = "Open Folder"; + } showOpenDialog({ - ...(_options.dialogOptions || {}), + ...(options.dialogOptions || {}), properties: { openDirectory: true, }, @@ -97,9 +103,9 @@ export class WindowsService implements IWindowsService { }); } - public async pickWorkspaceAndOpen(_options: INativeOpenDialogOptions): Promise { + public async pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise { showOpenDialog({ - ...(_options.dialogOptions || {}), + ...(options.dialogOptions || {}), properties: { openDirectory: true, },