diff --git a/app/src/plugins.ts b/app/src/plugins.ts index cd4b8e9a16e77ebea7196ac7ceeca616fffc5a0f..590d61747331ef7275ddc4dc8cd8e5b738ec2221 100644 --- a/app/src/plugins.ts +++ b/app/src/plugins.ts @@ -32,7 +32,7 @@ const userPluginsPath = path.join( Object.assign(window, { builtinPluginsPath, userPluginsPath }) nodeModule.globalPaths.unshift(builtinPluginsPath) -nodeModule.globalPaths.unshift(userPluginsPath) +nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules')) if (process.env.TERMINUS_PLUGINS) { process.env.TERMINUS_PLUGINS.split(':').map(x => nodeModule.globalPaths.unshift(normalizePath(x))) diff --git a/terminus-plugin-manager/src/components/pluginsSettingsTab.component.pug b/terminus-plugin-manager/src/components/pluginsSettingsTab.component.pug index 2d778b17e3edca81ece12d725dd6a0c2a9c6d9f9..df9d973fda660740ec6a0427708fc8b22e9a7b15 100644 --- a/terminus-plugin-manager/src/components/pluginsSettingsTab.component.pug +++ b/terminus-plugin-manager/src/components/pluginsSettingsTab.component.pug @@ -1,24 +1,37 @@ +.alert.alert-danger(*ngIf='errorMessage') + strong Error in {{erroredPlugin}}: + pre {{errorMessage}} + h3 Installed .list-group ng-container(*ngFor='let plugin of pluginManager.installedPlugins') .list-group-item.flex-column.align-items-start(*ngIf='knownUpgrades[plugin.name]') .d-flex.w-100 - h6.mr-auto.mb-0 {{plugin.name}} + h5.mr-auto.mb-0 {{plugin.name}} p.mb-0.mr-3 {{plugin.version}} - button.btn.btn-outline-primary((click)='upgradePlugin(plugin)') - i.fa.fa-arrow-up + button.btn.btn-outline-primary( + (click)='upgradePlugin(plugin)', + [disabled]='busy[plugin.name] != undefined' + ) + i.fa.fa-fw.fa-arrow-up(*ngIf='busy[plugin.name] != BusyState.Installing') + i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Installing') span Upgrade ({{knownUpgrades[plugin.name].version}}) - small.mb-0 {{plugin.description}} + small.text-muted.mb-0 {{plugin.description}} ng-container(*ngFor='let plugin of pluginManager.installedPlugins') .list-group-item.flex-column.align-items-start(*ngIf='!knownUpgrades[plugin.name]') .d-flex.w-100 - h6.mr-auto.mb-0 {{plugin.name}} + h5.mr-auto.mb-0 {{plugin.name}} p.mb-0.mr-3 {{plugin.version}} - button.btn.btn-outline-danger((click)='uninstallPlugin(plugin)', *ngIf='!plugin.isBuiltin') - i.fa.fa-trash-o - small.mb-0 {{plugin.description}} + button.btn.btn-outline-danger( + (click)='uninstallPlugin(plugin)', + *ngIf='!plugin.isBuiltin', + [disabled]='busy[plugin.name] != undefined' + ) + i.fa.fa-fw.fa-trash-o(*ngIf='busy[plugin.name] != BusyState.Uninstalling') + i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Uninstalling') + small.text-muted.mb-0 {{plugin.description}} h3.mt-4 Available @@ -30,17 +43,22 @@ h3.mt-4 Available input.form-control( type='text', '[(ngModel)]'='_1', - (ngModelChange)='availablePluginsQuery$.next(_1)', + (ngModelChange)='searchAvailable(_1)', placeholder='Search plugins' ) + .list-group(*ngIf='availablePlugins$') ng-container(*ngFor='let plugin of (availablePlugins$|async)') .list-group-item.flex-column.align-items-start(*ngIf='!isAlreadyInstalled(plugin)') .d-flex.w-100 - h6.mr-auto.mb-0 {{plugin.name}} + h5.mr-auto.mb-0 {{plugin.name}} p.mb-0.mr-3 {{plugin.version}} - button.btn.btn-outline-primary((click)='installPlugin(plugin)') - i.fa.fa-download - span Install - small.mb-0 {{plugin.description}} + button.btn.btn-outline-primary( + (click)='installPlugin(plugin)', + [disabled]='busy[plugin.name] != undefined' + ) + i.fa.fa-fw.fa-download(*ngIf='busy[plugin.name] != BusyState.Installing') + i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Installing') + span Install + small.text-muted.mb-0 {{plugin.description}} diff --git a/terminus-plugin-manager/src/components/pluginsSettingsTab.component.ts b/terminus-plugin-manager/src/components/pluginsSettingsTab.component.ts index 7d4204d0e61d21597d09d995cbdb0b700307c9d1..97ce114539d99e2c972a7fb2cd046d87fc7180e6 100644 --- a/terminus-plugin-manager/src/components/pluginsSettingsTab.component.ts +++ b/terminus-plugin-manager/src/components/pluginsSettingsTab.component.ts @@ -1,25 +1,28 @@ import { BehaviorSubject, Observable } from 'rxjs' -import * as fs from 'fs-promise' -import * as path from 'path' import * as semver from 'semver' -import { exec } from 'mz/child_process' -import { Component, Inject, ChangeDetectionStrategy } from '@angular/core' +import { Component, Input } from '@angular/core' +import { ConfigService } from 'terminus-core' import { IPluginInfo, PluginManagerService } from '../services/pluginManager.service' +enum BusyState { Installing, Uninstalling } + @Component({ template: require('./pluginsSettingsTab.component.pug'), styles: [require('./pluginsSettingsTab.component.scss')], - changeDetection: ChangeDetectionStrategy.OnPush, }) export class PluginsSettingsTabComponent { - availablePlugins$: Observable - availablePluginsQuery$ = new BehaviorSubject('') - availablePluginsReady = false - knownUpgrades: {[id: string]: IPluginInfo} = {} - busy: boolean + BusyState = BusyState + @Input() availablePlugins$: Observable + @Input() availablePluginsQuery$ = new BehaviorSubject('') + @Input() availablePluginsReady = false + @Input() knownUpgrades: {[id: string]: IPluginInfo} = {} + @Input() busy: {[id: string]: BusyState} = {} + @Input() erroredPlugin: string + @Input() errorMessage: string constructor ( + private config: ConfigService, public pluginManager: PluginManagerService ) { } @@ -41,12 +44,40 @@ export class PluginsSettingsTabComponent { }) } + searchAvailable (query: string) { + this.availablePluginsQuery$.next(query) + } + isAlreadyInstalled (plugin: IPluginInfo): boolean { return this.pluginManager.installedPlugins.some(x => x.name === plugin.name) } async installPlugin (plugin: IPluginInfo): Promise { - this.busy = true + this.busy[plugin.name] = BusyState.Installing + try { + await this.pluginManager.installPlugin(plugin) + delete this.busy[plugin.name] + this.config.requestRestart() + } catch (err) { + this.erroredPlugin = plugin.name + this.errorMessage = err + delete this.busy[plugin.name] + throw err + } + } + + async uninstallPlugin (plugin: IPluginInfo): Promise { + this.busy[plugin.name] = BusyState.Uninstalling + try { + await this.pluginManager.uninstallPlugin(plugin) + delete this.busy[plugin.name] + this.config.requestRestart() + } catch (err) { + this.erroredPlugin = plugin.name + this.errorMessage = err + delete this.busy[plugin.name] + throw err + } } async upgradePlugin (plugin: IPluginInfo): Promise { diff --git a/terminus-plugin-manager/src/services/pluginManager.service.ts b/terminus-plugin-manager/src/services/pluginManager.service.ts index ce1a7c69765ee3a5bdbc17018bb5720d7bbe9ac7..10e8c650a6a9b80edad22dc164b44feca6fbea3a 100644 --- a/terminus-plugin-manager/src/services/pluginManager.service.ts +++ b/terminus-plugin-manager/src/services/pluginManager.service.ts @@ -1,7 +1,7 @@ -import * as fs from 'fs-promise' import { Observable } from 'rxjs' import { Injectable } from '@angular/core' import { Logger, LogService } from 'terminus-core' +import { exec } from 'mz/child_process' import axios from 'axios' const NAME_PREFIX = 'terminus-' @@ -45,5 +45,14 @@ export class PluginManagerService { } async installPlugin (plugin: IPluginInfo) { + let result = await exec(`npm --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`) + console.log(result) + this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName) + this.installedPlugins.push(plugin) + } + + async uninstallPlugin (plugin: IPluginInfo) { + await exec(`npm --prefix "${this.userPluginsPath}" remove ${plugin.packageName}`) + this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName) } } diff --git a/terminus-terminal/src/components/terminalTab.component.ts b/terminus-terminal/src/components/terminalTab.component.ts index 58f666c4d135445bae6f82da5eff340d8c4648f1..6e7518a944f728267a1d39202eeed1c1c0e70c02 100644 --- a/terminus-terminal/src/components/terminalTab.component.ts +++ b/terminus-terminal/src/components/terminalTab.component.ts @@ -1,4 +1,4 @@ -import { Observable, BehaviorSubject, ReplaySubject, Subject, Subscription } from 'rxjs' +import { BehaviorSubject, ReplaySubject, Subject, Subscription } from 'rxjs' import 'rxjs/add/operator/bufferTime' import { Component, NgZone, Inject, Optional, ViewChild, HostBinding, Input } from '@angular/core' import { AppService, ConfigService, BaseTabComponent, ThemesService, HostAppService, Platform } from 'terminus-core' @@ -51,8 +51,9 @@ export class TerminalTabComponent extends BaseTabComponent { this.session = this.sessions.addSession( Object.assign({}, this.sessionOptions, resizeEvent) ) - this.session.output$.bufferTime(10).subscribe((datas) => { - let data = datas.join('') + // this.session.output$.bufferTime(10).subscribe((datas) => { + this.session.output$.subscribe(data => { + // let data = datas.join('') this.zone.run(() => { this.output$.next(data) })