diff --git a/app/src/app.module.ts b/app/src/app.module.ts index 6df9e91d..35f691fe 100644 --- a/app/src/app.module.ts +++ b/app/src/app.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' import { NgbModule } from '@ng-bootstrap/ng-bootstrap' -export async function getRootModule (plugins: any[]): Promise { +export function getRootModule (plugins: any[]) { let imports = [ BrowserModule, ...(plugins.map(x => x.default.forRoot ? x.default.forRoot() : x.default)), diff --git a/app/src/entry.ts b/app/src/entry.ts index a40a7193..5f7e6c40 100644 --- a/app/src/entry.ts +++ b/app/src/entry.ts @@ -6,11 +6,11 @@ import 'rxjs' // Always land on the start view location.hash = '' -import { enableProdMode } from '@angular/core' +import { enableProdMode, NgModuleRef } from '@angular/core' import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' import { getRootModule } from './app.module' -import { findPlugins, loadPlugins } from './plugins' +import { findPlugins, loadPlugins, IPluginInfo } from './plugins' if (process.platform === 'win32') { process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH @@ -22,10 +22,29 @@ if (require('electron-is-dev')) { enableProdMode() } -findPlugins().then(async plugins => { +async function bootstrap (plugins: IPluginInfo[], safeMode = false): Promise> { + if (safeMode) { + plugins = plugins.filter(x => x.isBuiltin) + } let pluginsModules = await loadPlugins(plugins, (current, total) => { (document.querySelector('.progress .bar') as HTMLElement).style.width = 100 * current / total + '%' }) - let module = await getRootModule(pluginsModules) - platformBrowserDynamic().bootstrapModule(module) + let module = getRootModule(pluginsModules) + return await platformBrowserDynamic().bootstrapModule(module) +} + +findPlugins().then(async plugins => { + console.log('Starting with plugins:', plugins) + try { + await bootstrap(plugins) + } catch (error) { + console.error('Angular bootstrapping error:', error) + console.warn('Trying safe mode') + window['safeModeReason'] = error + try { + await bootstrap(plugins, true) + } catch (error) { + console.error('Bootstrap failed:', error) + } + } }) diff --git a/app/src/plugins.ts b/app/src/plugins.ts index 4480275e..21a4200f 100644 --- a/app/src/plugins.ts +++ b/app/src/plugins.ts @@ -20,7 +20,7 @@ if (process.env.DEV) { nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath())) } -const builtinPluginsPath = path.join((process as any).resourcesPath, 'builtin-plugins') +const builtinPluginsPath = process.env.DEV ? path.dirname(require('electron').remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins') const userPluginsPath = path.join( require('electron').remote.app.getPath('appData'), diff --git a/terminus-core/src/components/appRoot.component.ts b/terminus-core/src/components/appRoot.component.ts index aa6a38df..30940085 100644 --- a/terminus-core/src/components/appRoot.component.ts +++ b/terminus-core/src/components/appRoot.component.ts @@ -1,5 +1,6 @@ import { Component, Inject, Input, HostListener } from '@angular/core' import { trigger, style, animate, transition, state } from '@angular/animations' +import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { ElectronService } from '../services/electron.service' import { HostAppService, Platform } from '../services/hostApp.service' @@ -10,6 +11,7 @@ import { DockingService } from '../services/docking.service' import { TabRecoveryService } from '../services/tabRecovery.service' import { ThemesService } from '../services/themes.service' +import { SafeModeModalComponent } from './safeModeModal.component' import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api' @Component({ @@ -62,6 +64,7 @@ export class AppRootComponent { public app: AppService, @Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[], log: LogService, + ngbModal: NgbModal, _themes: ThemesService, ) { this.logger = log.create('main') @@ -104,6 +107,10 @@ export class AppRootComponent { this.hotkeys.globalHotkey.subscribe(() => { this.onGlobalHotkey() }) + + if (window['safeModeReason']) { + ngbModal.open(SafeModeModalComponent) + } } onGlobalHotkey () { diff --git a/terminus-core/src/components/safeModeModal.component.pug b/terminus-core/src/components/safeModeModal.component.pug new file mode 100644 index 00000000..5d55cc7c --- /dev/null +++ b/terminus-core/src/components/safeModeModal.component.pug @@ -0,0 +1,7 @@ +.modal-body + .alert.alert-danger Terminus could not start with your plugins, so all third party plugins have been disabled in this session. The error was: + + pre {{error}} + +.modal-footer + button.btn.btn-outline-primary((click)='close()') Close diff --git a/terminus-core/src/components/safeModeModal.component.ts b/terminus-core/src/components/safeModeModal.component.ts new file mode 100644 index 00000000..33a5b605 --- /dev/null +++ b/terminus-core/src/components/safeModeModal.component.ts @@ -0,0 +1,19 @@ +import { Component, Input } from '@angular/core' +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' + +@Component({ + template: require('./safeModeModal.component.pug'), +}) +export class SafeModeModalComponent { + @Input() error: Error + + constructor ( + public modalInstance: NgbActiveModal, + ) { + this.error = window['safeModeReason'] + } + + close () { + this.modalInstance.dismiss() + } +} diff --git a/terminus-core/src/index.ts b/terminus-core/src/index.ts index 9c64e3ea..f222abdb 100644 --- a/terminus-core/src/index.ts +++ b/terminus-core/src/index.ts @@ -17,6 +17,7 @@ import { ThemesService } from './services/themes.service' import { AppRootComponent } from './components/appRoot.component' import { TabBodyComponent } from './components/tabBody.component' +import { SafeModeModalComponent } from './components/safeModeModal.component' import { StartPageComponent } from './components/startPage.component' import { TabHeaderComponent } from './components/tabHeader.component' import { TitleBarComponent } from './components/titleBar.component' @@ -65,9 +66,11 @@ const PROVIDERS = [ TitleBarComponent, WindowControlsComponent, RenameTabModalComponent, + SafeModeModalComponent, ], entryComponents: [ RenameTabModalComponent, + SafeModeModalComponent, ] }) export default class AppModule { diff --git a/terminus-core/src/theme.scss b/terminus-core/src/theme.scss index 7f172715..93d13eee 100644 --- a/terminus-core/src/theme.scss +++ b/terminus-core/src/theme.scss @@ -76,6 +76,13 @@ $list-group-border-color: rgba(255,255,255,.1); $list-group-hover-bg: rgba(255,255,255,.1); $list-group-link-active-bg: rgba(255,255,255,.2); +$pre-bg: $dropdown-bg; +$pre-color: $dropdown-link-color; + +$alert-danger-bg: $body-bg2; +$alert-danger-text: $red; +$alert-danger-border: $red; + @import '~bootstrap/scss/bootstrap.scss';