diff --git a/plugins/containers-plugin/package.json b/plugins/containers-plugin/package.json index 1cd4a82b8..e8e26ad53 100644 --- a/plugins/containers-plugin/package.json +++ b/plugins/containers-plugin/package.json @@ -1,6 +1,6 @@ { - "name": "@eclipse-che/theia-containers-plugin", - "publisher": "Eclipse Che", + "name": "containers-plugin", + "publisher": "@eclipse-che", "keywords": [ "theia-plugin" ], diff --git a/plugins/github-auth-plugin/package.json b/plugins/github-auth-plugin/package.json index 0da11b051..75e642b13 100644 --- a/plugins/github-auth-plugin/package.json +++ b/plugins/github-auth-plugin/package.json @@ -1,7 +1,7 @@ { - "name": "@eclipse-che/github-auth-plugin", + "name": "github-auth-plugin", "version": "0.0.1", - "publisher": "Eclipse Che", + "publisher": "@eclipse-che", "keywords": [ "theia-plugin" ], diff --git a/plugins/ports-plugin/package.json b/plugins/ports-plugin/package.json index 1ece27874..fde75df08 100644 --- a/plugins/ports-plugin/package.json +++ b/plugins/ports-plugin/package.json @@ -1,6 +1,6 @@ { - "name": "@eclipse-che/ports-plugin", - "publisher": "Eclipse Che", + "name": "ports-plugin", + "publisher": "@eclipse-che", "version": "0.0.1", "keywords": [ "theia-plugin" diff --git a/plugins/recommendations-plugin/package.json b/plugins/recommendations-plugin/package.json index 647daa52c..af3949637 100644 --- a/plugins/recommendations-plugin/package.json +++ b/plugins/recommendations-plugin/package.json @@ -1,6 +1,6 @@ { - "name": "@eclipse-che/recommendations-plugin", - "publisher": "Eclipse-Che", + "name": "recommendations-plugin", + "publisher": "@eclipse-che", "keywords": [ "theia-plugin" ], @@ -10,7 +10,7 @@ "src" ], "extensionDependencies": [ - "Eclipse Che.@eclipse-che/workspace-plugin" + "@eclipse-che.workspace-plugin" ], "dependencies": { "@eclipse-che/plugin": "0.0.1", diff --git a/plugins/recommendations-plugin/src/plugin/recommendations-plugin.ts b/plugins/recommendations-plugin/src/plugin/recommendations-plugin.ts index e9f2e9c5b..d6a772a58 100644 --- a/plugins/recommendations-plugin/src/plugin/recommendations-plugin.ts +++ b/plugins/recommendations-plugin/src/plugin/recommendations-plugin.ts @@ -75,7 +75,7 @@ export class RecommendationsPlugin { async enableRecommendationsPlugin(): Promise { // Bring featured recommendations after projects are cloned - const workspacePlugin = theia.plugins.getPlugin('Eclipse Che.@eclipse-che/workspace-plugin'); + const workspacePlugin = theia.plugins.getPlugin('@eclipse-che.workspace-plugin'); if (workspacePlugin && workspacePlugin.exports && workspacePlugin.exports.onDidCloneSources) { workspacePlugin.exports.onDidCloneSources(() => this.afterClone()); } diff --git a/plugins/resource-monitor-plugin/package.json b/plugins/resource-monitor-plugin/package.json index 8a53e7e98..ff7eeef84 100644 --- a/plugins/resource-monitor-plugin/package.json +++ b/plugins/resource-monitor-plugin/package.json @@ -1,6 +1,6 @@ { - "name": "@eclipse-che/resource-monitor-plugin", - "publisher": "Eclipse Che", + "name": "resource-monitor-plugin", + "publisher": "@eclipse-che", "keywords": [ "theia-plugin" ], diff --git a/plugins/ssh-plugin/package.json b/plugins/ssh-plugin/package.json index 3ba59bbe8..9da8bd5de 100644 --- a/plugins/ssh-plugin/package.json +++ b/plugins/ssh-plugin/package.json @@ -1,6 +1,6 @@ { - "name": "@eclipse-che/theia-ssh-plugin", - "publisher": "Eclipse Che", + "name": "ssh-plugin", + "publisher": "@eclipse-che", "keywords": [ "theia-plugin" ], diff --git a/plugins/task-plugin/package.json b/plugins/task-plugin/package.json index f64e0551b..3a0036385 100644 --- a/plugins/task-plugin/package.json +++ b/plugins/task-plugin/package.json @@ -1,6 +1,6 @@ { "name": "task-plugin", - "publisher": "Eclipse Che", + "publisher": "@eclipse-che", "keywords": [ "theia-plugin" ], diff --git a/plugins/telemetry-plugin/package.json b/plugins/telemetry-plugin/package.json index 000157570..60edfbcc9 100644 --- a/plugins/telemetry-plugin/package.json +++ b/plugins/telemetry-plugin/package.json @@ -1,7 +1,7 @@ { - "name": "@eclipse-che/telemetry-plugin", + "name": "telemetry-plugin", "version": "0.0.1", - "publisher": "Eclipse Che", + "publisher": "@eclipse-che", "keywords": [ "theia-plugin" ], diff --git a/plugins/welcome-plugin/package.json b/plugins/welcome-plugin/package.json index 87b312180..5a367495d 100644 --- a/plugins/welcome-plugin/package.json +++ b/plugins/welcome-plugin/package.json @@ -1,7 +1,7 @@ { - "name": "@eclipse-che/welcome-plugin", + "name": "welcome-plugin", "version": "0.0.1", - "publisher": "Eclipse Che", + "publisher": "@eclipse-che", "keywords": [ "theia-plugin" ], @@ -28,13 +28,17 @@ } }, "dependencies": { - "@fortawesome/fontawesome-free": "5.6.3" + "@fortawesome/fontawesome-free": "5.6.3", + "fs-extra": "7.0.1" }, "devDependencies": { "@theia/plugin": "next", "@theia/plugin-packager": "latest", "@eclipse-che/plugin": "0.0.1" }, + "extensionDependencies": [ + "@eclipse-che.workspace-plugin" + ], "scripts": { "prepare": "yarn clean && yarn build && yarn test", "clean": "rimraf lib", diff --git a/plugins/welcome-plugin/src/commands.ts b/plugins/welcome-plugin/src/commands.ts new file mode 100644 index 000000000..0e866b08d --- /dev/null +++ b/plugins/welcome-plugin/src/commands.ts @@ -0,0 +1,26 @@ +/********************************************************************** + * Copyright (c) 2021 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import * as theia from '@theia/plugin'; + +export namespace Commands { + export const SHOW_WELCOME: theia.CommandDescription = { + id: 'welcome:show_welcome', + label: 'Welcome: Show Welcome...', + }; + + export const ENABLE_WELCOME: theia.CommandDescription = { + id: 'welcome:enable', + }; + + export const DISABLE_WELCOME: theia.CommandDescription = { + id: 'welcome:disable', + }; +} diff --git a/plugins/welcome-plugin/src/devfile.ts b/plugins/welcome-plugin/src/devfile.ts new file mode 100644 index 000000000..18820b3b8 --- /dev/null +++ b/plugins/welcome-plugin/src/devfile.ts @@ -0,0 +1,39 @@ +/********************************************************************** + * Copyright (c) 2021 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import * as che from '@eclipse-che/plugin'; + +export const WELCOME_ENABLED = 'extensions.welcome'; + +export async function isWelcomeEnabled(): Promise { + const workspace = await che.workspace.getCurrentWorkspace(); + // always has a devfile now + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const devfile = workspace.devfile!; + const attributes = devfile.attributes || {}; + const welcome = attributes[WELCOME_ENABLED] || 'true'; + return welcome !== 'false'; +} + +export async function enableWelcome(enable: boolean): Promise { + const workspace = await che.workspace.getCurrentWorkspace(); + // always has a devfile now + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const devfile = workspace.devfile!; + devfile.attributes = devfile.attributes || {}; + devfile.attributes[WELCOME_ENABLED] = '' + enable; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await che.workspace.update(workspace.id!, workspace); +} + +export async function isMultiroot(): Promise { + const devfile = await che.devfile.get(); + return devfile.metadata?.attributes?.multiRoot !== 'off'; +} diff --git a/plugins/welcome-plugin/src/readme.ts b/plugins/welcome-plugin/src/readme.ts new file mode 100644 index 000000000..6edaaacab --- /dev/null +++ b/plugins/welcome-plugin/src/readme.ts @@ -0,0 +1,82 @@ +/********************************************************************** + * Copyright (c) 2021 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import * as fs from 'fs-extra'; +import * as path from 'path'; +import * as theia from '@theia/plugin'; + +import { isMultiroot } from './devfile'; + +export class Readme { + processed: string[] = []; + + constructor(protected context: theia.PluginContext) {} + + async seekAndOpen(): Promise { + if (await isMultiroot()) { + this.context.subscriptions.push( + theia.workspace.onDidChangeWorkspaceFolders( + event => this.handleReadmeFiles(event.added), + undefined, + this.context.subscriptions + ) + ); + } else { + const workspacePlugin = theia.plugins.getPlugin('@eclipse-che.workspace-plugin'); + if (workspacePlugin && workspacePlugin.exports) { + this.context.subscriptions.push( + workspacePlugin.exports.onDidCloneSources( + () => this.handleReadmeFiles(), + undefined, + this.context.subscriptions + ) + ); + } else { + this.handleReadmeFiles(); + } + } + } + + async handleReadmeFiles(roots?: theia.WorkspaceFolder[]): Promise { + roots = roots ? roots : theia.workspace.workspaceFolders; + if (!roots || roots.length < 1) { + return; + } + + for (const root of roots) { + this.openReadme(root.uri.fsPath); + } + } + + async openReadme(projectPath: string): Promise { + if (this.processed.some(value => value === projectPath)) { + return; + } + + this.processed.push(projectPath); + + const readmePath = path.join(projectPath, 'README.md'); + if (await fs.pathExists(projectPath)) { + const openPath = theia.Uri.parse(`file://${readmePath}?open-handler=code-editor-preview`); + try { + const doc = await theia.workspace.openTextDocument(openPath); + if (doc) { + await theia.window.showTextDocument(doc); + } + } catch (err) { + // Ignore the error. + // `theia.window.showTextDocument` throws an error with message `Failed to show text document` + // But works as expected. + // It's because `?open-handler=code-editor-preview` was added at the end of the URI + // to open preview instead of the editor + } + } + } +} diff --git a/plugins/welcome-plugin/src/welcome-page.ts b/plugins/welcome-plugin/src/welcome-panel.ts similarity index 59% rename from plugins/welcome-plugin/src/welcome-page.ts rename to plugins/welcome-plugin/src/welcome-panel.ts index 5a4bb3f5f..aeef427b9 100644 --- a/plugins/welcome-plugin/src/welcome-page.ts +++ b/plugins/welcome-plugin/src/welcome-panel.ts @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2020 Red Hat, Inc. + * Copyright (c) 2021 Red Hat, Inc. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -9,13 +9,137 @@ ***********************************************************************/ import * as che from '@eclipse-che/plugin'; +import * as path from 'path'; import * as theia from '@theia/plugin'; -/** - * Welcome Page - */ -export class WelcomePage { - protected renderHeader(): string { +import { isWelcomeEnabled } from './devfile'; + +export const welcomePageViewType = 'WelcomePage'; + +export class WelcomePanel { + webviewPanel: theia.WebviewPanel | undefined; + + constructor(private context: theia.PluginContext) {} + + async show(): Promise { + if (this.webviewPanel) { + this.webviewPanel.reveal(); + return; + } + + // Open Welcome tab + this.webviewPanel = theia.window.createWebviewPanel( + welcomePageViewType, + che.product.name, + { viewColumn: theia.ViewColumn.One, preserveFocus: false }, + { + // Enable javascript in the webview + enableScripts: true, + + // And restrict the webview to only loading content from our extension's `media` directory. + localResourceRoots: [ + theia.Uri.file(path.join(this.context.extensionPath, 'resources')), + theia.Uri.file(path.join(this.context.extensionPath, 'node_modules/@fortawesome')), + ], + } + ); + + await this.render(); + + // Handle messages from the webview + this.webviewPanel.webview.onDidReceiveMessage( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (message: any) => { + switch (message.command) { + case 'alert': + theia.window.showErrorMessage(message.text); + return; + case 'executeCommand': + theia.commands.executeCommand(message.commandName); + return; + } + }, + undefined, + this.context.subscriptions + ); + + this.webviewPanel.iconPath = theia.Uri.parse(che.product.icon); + + this.webviewPanel.onDidDispose(() => { + this.webviewPanel = undefined; + }); + } + + async render(): Promise { + if (!this.webviewPanel) { + return; + } + + // Local path to main script run in the webview + const scriptPathOnDisk = theia.Uri.file(path.join(this.context.extensionPath, 'resources', 'welcome-page.js')); + // And the uri we use to load this script in the webview + const scriptUri = scriptPathOnDisk.with({ scheme: 'theia-resource' }); + + // Local path to main script run in the webview + const cssPathOnDisk = theia.Uri.file(path.join(this.context.extensionPath, 'resources', 'welcome-page.css')); + // And the uri we use to load this script in the webview + const cssUri = cssPathOnDisk.with({ scheme: 'theia-resource' }); + + // const welcomePage = new WelcomePage(); + // const rendering = await welcomePage.render(); + + const html = ` + + + + + + + + Welcome Page + + + + ${await this.renderBody()} + + + `; + + this.webviewPanel.webview.html = html; + } + + private async renderBody(): Promise { + return `
+ ${await this.renderHeader()} +
+
+ ${await this.renderStart()} +
+
+
+
+ ${await this.renderOpen()} +
+
+
+
+ ${await this.renderSettings()} +
+
+
+
+ ${await this.renderHelp()} +
+
+
+
+ ${await this.renderControl()} +
+
+
`; + } + + private async renderHeader(): Promise { const logoDark = typeof che.product.logo === 'object' ? che.product.logo.dark : che.product.logo; const logoLight = typeof che.product.logo === 'object' ? che.product.logo.light : che.product.logo; @@ -35,6 +159,29 @@ export class WelcomePage { `; } + private async renderStart(): Promise { + // eslint-disable-next-line max-len + const newFile = `
+ New File... + ${await this.renderCommandKeyBinding('file.newFile')}
`; + + // eslint-disable-next-line max-len + const gitClone = `
+ Git Clone... + ${await this.renderCommandKeyBinding('git.clone')}
`; + + return `
+

New

+
+ ${newFile} +
+
+ ${gitClone} +
+ +
`; + } + private async renderCommandKeyBinding(commandId: string): Promise { const availableKeys = await theia.commands.getKeyBinding(commandId); @@ -74,25 +221,18 @@ export class WelcomePage { return ''; } - private async renderStart(): Promise { - // eslint-disable-next-line max-len - const newFile = `
New File...${await this.renderCommandKeyBinding( - 'file.newFile' - )}
`; - // eslint-disable-next-line max-len - const gitClone = `
Git Clone...${await this.renderCommandKeyBinding( - 'git.clone' - )}
`; - return `
-

New

-
- ${newFile} -
-
- ${gitClone} -
+ private async renderControl(): Promise { + const label = 'Show this page on workspace startup'; -
`; + const checked = await isWelcomeEnabled(); + + return `
+
+ + +
+
`; } private async renderOpen(): Promise { @@ -169,30 +309,4 @@ export class WelcomePage { ${html} `; } - - public async render(): Promise { - return `
- ${this.renderHeader()} -
-
- ${await this.renderStart()} -
-
-
-
- ${await this.renderOpen()} -
-
-
-
- ${await this.renderSettings()} -
-
-
-
- ${await this.renderHelp()} -
-
-
`; - } } diff --git a/plugins/welcome-plugin/src/welcome-plugin.ts b/plugins/welcome-plugin/src/welcome-plugin.ts index f937babd8..6417d9034 100644 --- a/plugins/welcome-plugin/src/welcome-plugin.ts +++ b/plugins/welcome-plugin/src/welcome-plugin.ts @@ -8,166 +8,51 @@ * SPDX-License-Identifier: EPL-2.0 ***********************************************************************/ -import * as che from '@eclipse-che/plugin'; -import * as path from 'path'; import * as theia from '@theia/plugin'; -import { WelcomePage } from './welcome-page'; +import { enableWelcome, isWelcomeEnabled } from './devfile'; -export namespace Settings { - export const CHE_CONFIGURATION = 'che'; - export const SHOW_WELCOME_PAGE = 'welcome.enable'; -} - -export const welcomePageViewType = 'WelcomePage'; - -async function getHtmlForWebview(context: theia.PluginContext): Promise { - // Local path to main script run in the webview - const scriptPathOnDisk = theia.Uri.file(path.join(context.extensionPath, 'resources', 'welcome-page.js')); - // And the uri we use to load this script in the webview - const scriptUri = scriptPathOnDisk.with({ scheme: 'theia-resource' }); - - // Local path to main script run in the webview - const cssPathOnDisk = theia.Uri.file(path.join(context.extensionPath, 'resources', 'welcome-page.css')); - // And the uri we use to load this script in the webview - const cssUri = cssPathOnDisk.with({ scheme: 'theia-resource' }); +import { Commands } from './commands'; +import { Readme } from './readme'; +import { WelcomePanel } from './welcome-panel'; - const welcomePage = new WelcomePage(); - const rendering = await welcomePage.render(); - return ` - - - - - - - - Welcome Page - - - - ${rendering} - - - `; +export async function start(context: theia.PluginContext): Promise { + await new WelcomePlugin(context).start(); } -// Open Readme file is there is one -export async function handleReadmeFiles( - readmeHandledCallback?: () => void, - roots?: theia.WorkspaceFolder[] -): Promise { - roots = roots ? roots : theia.workspace.workspaceFolders; - if (!roots || roots.length < 1) { - return; - } - - const children = await theia.workspace.findFiles('README.md', 'node_modules/**', 1); - const updatedChildren = children.filter((child: theia.Uri) => { - if (child.fsPath.indexOf('node_modules') === -1) { - return child; - } - }); - - if (updatedChildren.length < 1) { - return; - } - - const openPath = theia.Uri.parse(updatedChildren[0] + '?open-handler=code-editor-preview'); - const doc: theia.TextDocument | undefined = await theia.workspace.openTextDocument(openPath); - if (doc) { - theia.window.showTextDocument(doc); - - if (readmeHandledCallback) { - readmeHandledCallback(); - } - } -} - -export async function addPanel(context: theia.PluginContext): Promise { - // Open Welcome tab - const currentPanel = theia.window.createWebviewPanel( - welcomePageViewType, - che.product.name, - { viewColumn: theia.ViewColumn.One, preserveFocus: false }, - { - // Enable javascript in the webview - enableScripts: true, - - // And restrict the webview to only loading content from our extension's `media` directory. - localResourceRoots: [ - theia.Uri.file(path.join(context.extensionPath, 'resources')), - theia.Uri.file(path.join(context.extensionPath, 'node_modules/@fortawesome')), - ], - } - ); - - currentPanel.webview.html = await getHtmlForWebview(context); - - // Handle messages from the webview - currentPanel.webview.onDidReceiveMessage( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (message: any) => { - switch (message.command) { - case 'alert': - theia.window.showErrorMessage(message.text); - return; - case 'executeCommand': - theia.commands.executeCommand(message.commandName); - return; - } - }, - undefined, - context.subscriptions - ); +export function stop(): void {} - currentPanel.iconPath = theia.Uri.parse(che.product.icon); -} +class WelcomePlugin { + constructor(protected context: theia.PluginContext) {} -export function start(context: theia.PluginContext): void { - let showWelcomePage: boolean | undefined = true; + async start(): Promise { + const welcome = new WelcomePanel(this.context); - const configuration = theia.workspace.getConfiguration(Settings.CHE_CONFIGURATION); - if (configuration) { - showWelcomePage = configuration.get(Settings.SHOW_WELCOME_PAGE); - } + this.context.subscriptions.push( + theia.commands.registerCommand(Commands.SHOW_WELCOME, async () => { + await welcome.show(); + }) + ); - if (!showWelcomePage || theia.window.visibleTextEditors.length > 0) { - return; - } + this.context.subscriptions.push( + theia.commands.registerCommand(Commands.ENABLE_WELCOME, async () => { + await enableWelcome(true); + }) + ); - let cloneSourcesDisposable: theia.Disposable | undefined = undefined; - setTimeout(async () => { - addPanel(context); + this.context.subscriptions.push( + theia.commands.registerCommand(Commands.DISABLE_WELCOME, async () => { + await enableWelcome(false); + }) + ); - const workspacePlugin = theia.plugins.getPlugin('Eclipse Che.@eclipse-che/workspace-plugin'); - if (workspacePlugin && workspacePlugin.exports) { - // it handles the case when the multi-root mode is OFF - // we should remove this logic when we switch to the multi-root mode is ON by default - cloneSourcesDisposable = workspacePlugin.exports.onDidCloneSources( - () => handleReadmeFiles(readmeHandledCallback), - undefined, - context.subscriptions - ); - } else { - handleReadmeFiles(); + if (theia.window.visibleTextEditors.length > 0) { + return; } - }, 100); - - // handles the case when the multi-root mode is ON - const changeWorkspaceFoldersDisposable = theia.workspace.onDidChangeWorkspaceFolders( - event => handleReadmeFiles(readmeHandledCallback, event.added), - undefined, - context.subscriptions - ); - - const readmeHandledCallback = () => { - changeWorkspaceFoldersDisposable.dispose(); - if (cloneSourcesDisposable) { - cloneSourcesDisposable.dispose(); + if (await isWelcomeEnabled()) { + await welcome.show(); + new Readme(this.context).seekAndOpen(); } - }; + } } - -export function stop(): void {} diff --git a/plugins/workspace-plugin/package.json b/plugins/workspace-plugin/package.json index a0c45f1fa..1f2b9bc72 100644 --- a/plugins/workspace-plugin/package.json +++ b/plugins/workspace-plugin/package.json @@ -1,6 +1,6 @@ { - "name": "@eclipse-che/workspace-plugin", - "publisher": "Eclipse Che", + "name": "workspace-plugin", + "publisher": "@eclipse-che", "version": "0.0.1", "keywords": [ "theia-plugin" @@ -19,7 +19,7 @@ "@theia/plugin-packager": "latest" }, "extensionDependencies": [ - "Eclipse Che.@eclipse-che/theia-ssh-plugin", + "@eclipse-che.ssh-plugin", "@eclipse-che.ext-plugin" ], "scripts": { diff --git a/plugins/workspace-plugin/src/ssh.ts b/plugins/workspace-plugin/src/ssh.ts index 650fb0282..c0335bd5c 100644 --- a/plugins/workspace-plugin/src/ssh.ts +++ b/plugins/workspace-plugin/src/ssh.ts @@ -10,7 +10,7 @@ import * as theia from '@theia/plugin'; -const SSH_PLUGIN_ID = 'Eclipse Che.@eclipse-che/theia-ssh-plugin'; +const SSH_PLUGIN_ID = '@eclipse-che.ssh-plugin'; export interface SSHAgentConfig { sshAgentPid?: string;