From ecc5041e313076fb770836debeb38ee04afcc22b Mon Sep 17 00:00:00 2001 From: Sanjula Ganepola Date: Tue, 24 Sep 2024 23:47:53 -0400 Subject: [PATCH] initial work for components view Signed-off-by: Sanjula Ganepola --- package.json | 40 ++++++++++++++-- src/extension.ts | 27 ++++------- src/types.ts | 12 +++++ src/views/component/component.ts | 24 ++++++++++ .../component/componentTreeDataProvider.ts | 46 +++++++++++++++++++ src/views/componentManager.ts | 32 +++++++++++++ src/views/decorationProvider.ts | 28 +++++++++++ src/views/githubLocalActionsTreeItem.ts | 7 +++ 8 files changed, 192 insertions(+), 24 deletions(-) create mode 100644 src/types.ts create mode 100644 src/views/component/component.ts create mode 100644 src/views/component/componentTreeDataProvider.ts create mode 100644 src/views/componentManager.ts create mode 100644 src/views/decorationProvider.ts create mode 100644 src/views/githubLocalActionsTreeItem.ts diff --git a/package.json b/package.json index 4747acf..fdb82bb 100644 --- a/package.json +++ b/package.json @@ -41,24 +41,54 @@ "views": { "github-local-actions-container": [ { - "id": "status", - "name": "Status" + "id": "component", + "name": "Component", + "icon": "$(telescope)" }, { "id": "workflows", - "name": "Workflows" + "name": "Workflows", + "icon": "$(remote-explorer)" }, { "id": "events", - "name": "Events" + "name": "Events", + "icon": "$(rocket)" }, { "id": "settings", - "name": "Settings" + "name": "Settings", + "icon": "$(server-environment)" } ] }, "commands": [ + ], + "colors": [ + { + "id": "GitHubLocalActions.enabled", + "description": "Color for a component success state", + "defaults": { + "dark": "#89d185", + "light": "#89d185" + } + }, + { + "id": "GitHubLocalActions.warning", + "description": "Color for a component warning state", + "defaults": { + "dark": "#cca700", + "light": "#cca700" + } + }, + { + "id": "GitHubLocalActions.disabled", + "description": "Color for a component disabled state", + "defaults": { + "dark": "#f48771", + "light": "#f48771" + } + } ] }, "scripts": { diff --git a/src/extension.ts b/src/extension.ts index 3c1c68a..470ddab 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,26 +1,15 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below import * as vscode from 'vscode'; +import { window } from 'vscode'; +import ComponentTreeDataProvider from './views/component/componentTreeDataProvider'; -// This method is called when your extension is activated -// Your extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext) { - - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated console.log('Congratulations, your extension "github-local-actions" is now active!'); - // The command has been defined in the package.json file - // Now provide the implementation of the command with registerCommand - // The commandId parameter must match the command field in package.json - const disposable = vscode.commands.registerCommand('github-local-actions.helloWorld', () => { - // The code you place here will be executed every time your command is executed - // Display a message box to the user - vscode.window.showInformationMessage('Hello World from github-local-actions!'); - }); - - context.subscriptions.push(disposable); + const componentTreeDataProvider = new ComponentTreeDataProvider(context); + const componentTreeView = window.createTreeView(ComponentTreeDataProvider.VIEW_ID, { treeDataProvider: componentTreeDataProvider }); + context.subscriptions.push( + componentTreeView + ); } -// This method is called when your extension is deactivated -export function deactivate() {} +export function deactivate() { } diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..d99adcb --- /dev/null +++ b/src/types.ts @@ -0,0 +1,12 @@ +export interface Component { + name: string, + status: Status, + icon: string, + message?: string +} + +export enum Status { + Enabled = 'Enabled', + Warning = 'Warning', + Disabled = 'Disabled' +} \ No newline at end of file diff --git a/src/views/component/component.ts b/src/views/component/component.ts new file mode 100644 index 0000000..20ce3dd --- /dev/null +++ b/src/views/component/component.ts @@ -0,0 +1,24 @@ +import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode"; +import { Component } from "../../types"; +import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; + +export default class ComponentTreeItem extends TreeItem implements GithubLocalActionsTreeItem { + static contextValue = 'component'; + component: Component; + + constructor(component: Component) { + super(component.name, TreeItemCollapsibleState.Collapsed); + this.component = component; + this.collapsibleState = TreeItemCollapsibleState.None; + this.contextValue = ComponentTreeItem.contextValue; + this.iconPath = new ThemeIcon(component.icon); + this.resourceUri = Uri.parse(`${ComponentTreeItem.contextValue}:${component.name}?status=${component.status}`, true); + this.tooltip = `Name: ${component.name}\n` + + `Status: ${component.status}\n` + + (component.message ? `Message: ${component.message}` : ``); + } + + async getChildren(): Promise { + return []; + } +} \ No newline at end of file diff --git a/src/views/component/componentTreeDataProvider.ts b/src/views/component/componentTreeDataProvider.ts new file mode 100644 index 0000000..f4f8128 --- /dev/null +++ b/src/views/component/componentTreeDataProvider.ts @@ -0,0 +1,46 @@ +import { CancellationToken, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, window } from "vscode"; +import { ComponentManager } from "../componentManager"; +import { DecorationProvider } from "../decorationProvider"; +import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; +import ComponentTreeItem from "./component"; + +export default class ComponentTreeDataProvider implements TreeDataProvider { + private _onDidChangeTreeData = new EventEmitter(); + readonly onDidChangeTreeData = this._onDidChangeTreeData.event; + public static VIEW_ID = 'component'; + private componentManager: ComponentManager; + + constructor(context: ExtensionContext) { + this.componentManager = new ComponentManager(); + + const decorationProvider = new DecorationProvider(); + context.subscriptions.push( + window.registerFileDecorationProvider(decorationProvider), + ); + } + + refresh(element?: GithubLocalActionsTreeItem) { + this._onDidChangeTreeData.fire(element); + } + + getTreeItem(element: GithubLocalActionsTreeItem): GithubLocalActionsTreeItem | Thenable { + return element; + } + + async resolveTreeItem(item: TreeItem, element: GithubLocalActionsTreeItem, token: CancellationToken): Promise { + if (element.getToolTip) { + element.tooltip = await element.getToolTip(); + } + + return element; + } + + async getChildren(element?: GithubLocalActionsTreeItem): Promise { + if (element) { + return element.getChildren(); + } else { + const components = await this.componentManager.getComponents(); + return components.map(component => new ComponentTreeItem(component)); + } + } +} \ No newline at end of file diff --git a/src/views/componentManager.ts b/src/views/componentManager.ts new file mode 100644 index 0000000..10908fc --- /dev/null +++ b/src/views/componentManager.ts @@ -0,0 +1,32 @@ +import { Component, Status } from "../types"; + +export class ComponentManager { + components: Component[] = [ + { + name: 'nektos/act', + status: Status.Enabled, + icon: 'package' + }, + { + name: 'Docker Engine', + status: Status.Disabled, + icon: 'dashboard' + }, + { + name: 'GitHub Actions Extension', + status: Status.Warning, + icon: 'extensions', + message: 'GitHub Actions extension is not required but is recommended to take advantage of workflow editor features' + }, + { + name: 'GitHub Actions CLI', + status: Status.Warning, + icon: 'terminal', + message: 'GitHub Actions CLI is not required but is recommended if you plan to use it to retrieve GitHub tokens' + } + ]; + + async getComponents(): Promise { + return this.components; + } +} \ No newline at end of file diff --git a/src/views/decorationProvider.ts b/src/views/decorationProvider.ts new file mode 100644 index 0000000..3793fcd --- /dev/null +++ b/src/views/decorationProvider.ts @@ -0,0 +1,28 @@ +import { CancellationToken, Event, FileDecoration, FileDecorationProvider, ProviderResult, ThemeColor, Uri } from "vscode"; +import { Status } from "../types"; +import ComponentTreeItem from "./component/component"; + +export class DecorationProvider implements FileDecorationProvider { + onDidChangeFileDecorations?: Event | undefined; + provideFileDecoration(uri: Uri, token: CancellationToken): ProviderResult { + if (uri.scheme === ComponentTreeItem.contextValue) { + const params = new URLSearchParams(uri.query); + if (params.get('status') === Status.Enabled) { + return { + badge: '✅', + color: new ThemeColor('GitHubLocalActions.enabled') + }; + } else if (params.get('status') === Status.Warning) { + return { + badge: '⚠️', + color: new ThemeColor('GitHubLocalActions.warning') + }; + } else if (params.get('status') === Status.Disabled) { + return { + badge: '❌', + color: new ThemeColor('GitHubLocalActions.disabled') + }; + } + } + } +} \ No newline at end of file diff --git a/src/views/githubLocalActionsTreeItem.ts b/src/views/githubLocalActionsTreeItem.ts new file mode 100644 index 0000000..7837b85 --- /dev/null +++ b/src/views/githubLocalActionsTreeItem.ts @@ -0,0 +1,7 @@ +import { MarkdownString, TreeItem } from "vscode"; + +export interface GithubLocalActionsTreeItem extends TreeItem { + getChildren: () => GithubLocalActionsTreeItem[] | Promise; + + getToolTip?: () => Promise; +} \ No newline at end of file