mirror of
https://github.com/Eugeny/tabby.git
synced 2024-12-23 10:32:29 +03:00
wip
This commit is contained in:
parent
c0f7cd9a7a
commit
73722c0b2f
@ -18,16 +18,21 @@ if (process.env.DEV) {
|
||||
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
|
||||
}
|
||||
|
||||
nodeModule.globalPaths.unshift(path.join(
|
||||
const builtinPluginsPath = path.join(
|
||||
path.dirname(require('electron').remote.app.getPath('exe')),
|
||||
(process.platform === 'darwin') ? '../Resources' : 'resources',
|
||||
'builtin-plugins/node_modules',
|
||||
))
|
||||
nodeModule.globalPaths.unshift(path.join(
|
||||
)
|
||||
|
||||
const userPluginsPath = path.join(
|
||||
require('electron').remote.app.getPath('appData'),
|
||||
'terminus',
|
||||
'plugins',
|
||||
))
|
||||
)
|
||||
|
||||
Object.assign(window, { builtinPluginsPath, userPluginsPath })
|
||||
nodeModule.globalPaths.unshift(builtinPluginsPath)
|
||||
nodeModule.globalPaths.unshift(userPluginsPath)
|
||||
|
||||
if (process.env.TERMINUS_PLUGINS) {
|
||||
process.env.TERMINUS_PLUGINS.split(':').map(x => nodeModule.globalPaths.unshift(normalizePath(x)))
|
||||
@ -35,15 +40,20 @@ if (process.env.TERMINUS_PLUGINS) {
|
||||
|
||||
export declare type ProgressCallback = (current, total) => void
|
||||
|
||||
interface IPluginEntry {
|
||||
export interface IPluginInfo {
|
||||
name: string
|
||||
path: string
|
||||
info: any
|
||||
description: string
|
||||
packageName: string
|
||||
isBuiltin: boolean
|
||||
version: string
|
||||
homepage?: string
|
||||
path?: string
|
||||
info?: any
|
||||
}
|
||||
|
||||
export async function findPlugins (): Promise<IPluginEntry[]> {
|
||||
export async function findPlugins (): Promise<IPluginInfo[]> {
|
||||
let paths = nodeModule.globalPaths
|
||||
let foundPlugins: IPluginEntry[] = []
|
||||
let foundPlugins: IPluginInfo[] = []
|
||||
|
||||
for (let pluginDir of paths) {
|
||||
pluginDir = normalizePath(pluginDir)
|
||||
@ -63,10 +73,16 @@ export async function findPlugins (): Promise<IPluginEntry[]> {
|
||||
}
|
||||
|
||||
try {
|
||||
let info = await fs.readJson(infoPath)
|
||||
console.log(pluginDir, builtinPluginsPath)
|
||||
foundPlugins.push({
|
||||
name: pluginName,
|
||||
name: pluginName.substring('terminus-'.length),
|
||||
packageName: pluginName,
|
||||
isBuiltin: pluginDir === builtinPluginsPath,
|
||||
version: info.version,
|
||||
description: info.description,
|
||||
path: pluginPath,
|
||||
info: await fs.readJson(infoPath),
|
||||
info,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Cannot load package info for', pluginName)
|
||||
@ -74,10 +90,11 @@ export async function findPlugins (): Promise<IPluginEntry[]> {
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).installedPlugins = foundPlugins
|
||||
return foundPlugins
|
||||
}
|
||||
|
||||
export async function loadPlugins (foundPlugins: IPluginEntry[], progress: ProgressCallback): Promise<any[]> {
|
||||
export async function loadPlugins (foundPlugins: IPluginInfo[], progress: ProgressCallback): Promise<any[]> {
|
||||
let plugins: any[] = []
|
||||
progress(0, 1)
|
||||
let index = 0
|
||||
|
@ -8,9 +8,7 @@ exports.builtinPlugins = [
|
||||
'terminus-core',
|
||||
'terminus-settings',
|
||||
'terminus-terminal',
|
||||
'terminus-clickable-links',
|
||||
'terminus-community-color-schemes',
|
||||
'terminus-theme-hype',
|
||||
]
|
||||
exports.version = appInfo.version
|
||||
exports.electronVersion = pkgInfo.devDependencies.electron
|
||||
|
2
terminus-clickable-links/.gitignore
vendored
2
terminus-clickable-links/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
dist
|
||||
node_modules
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "terminus-clickable-links",
|
||||
"version": "0.0.1",
|
||||
"description": "Makes URLs and file paths clickable in Terminus",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "webpack --progress --color",
|
||||
"watch": "webpack --progress --color --watch"
|
||||
},
|
||||
"files": ["dist"],
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"awesome-typescript-loader": "^3.1.2",
|
||||
"typescript": "^2.2.2",
|
||||
"webpack": "^2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "^4.0.1",
|
||||
"terminus-core": "*",
|
||||
"terminus-terminal": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"untildify": "^3.0.2"
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
export abstract class LinkHandler {
|
||||
regex: string
|
||||
convert (uri: string): string { return uri }
|
||||
verify (_uri: string): boolean { return true }
|
||||
abstract handle (uri: string): void
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
import { Observable } from 'rxjs'
|
||||
import { Inject, Injectable } from '@angular/core'
|
||||
import { TerminalDecorator, TerminalTabComponent } from 'terminus-terminal'
|
||||
|
||||
import { LinkHandler } from './api'
|
||||
|
||||
@Injectable()
|
||||
export class LinkHighlighterDecorator extends TerminalDecorator {
|
||||
constructor (@Inject(LinkHandler) private handlers: LinkHandler[]) {
|
||||
super()
|
||||
}
|
||||
|
||||
attach (terminal: TerminalTabComponent): void {
|
||||
return
|
||||
terminal.contentUpdated$
|
||||
.throttle(() => Observable.from([500]))
|
||||
.subscribe(() => {
|
||||
//this.insertLinks(terminal.hterm.screen_)
|
||||
})
|
||||
}
|
||||
|
||||
insertLinks (screen) {
|
||||
if ('#text' === screen.cursorNode_.nodeName) {
|
||||
// replace text node to element
|
||||
const cursorNode = document.createElement('span')
|
||||
cursorNode.textContent = screen.cursorNode_.textContent
|
||||
screen.cursorRowNode_.replaceChild(cursorNode, screen.cursorNode_)
|
||||
screen.cursorNode_ = cursorNode
|
||||
}
|
||||
|
||||
const traverse = (parentNode: Node) => {
|
||||
Array.from(parentNode.childNodes).forEach((node) => {
|
||||
if (node.nodeName === '#text') {
|
||||
parentNode.replaceChild(this.urlizeNode(node), node)
|
||||
} else if (node.nodeName !== 'A') {
|
||||
traverse(node)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
screen.rowsArray.forEach((x) => traverse(x))
|
||||
}
|
||||
|
||||
urlizeNode (node) {
|
||||
let matches = []
|
||||
this.handlers.forEach((handler) => {
|
||||
let regex = new RegExp(handler.regex, 'gi')
|
||||
while (true) {
|
||||
let match = regex.exec(node.textContent)
|
||||
if (!match) {
|
||||
break
|
||||
}
|
||||
let uri = handler.convert(match[0])
|
||||
if (!handler.verify(uri)) {
|
||||
continue
|
||||
}
|
||||
matches.push({
|
||||
start: regex.lastIndex - match[0].length,
|
||||
end: regex.lastIndex,
|
||||
text: match[0],
|
||||
uri,
|
||||
handler
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (matches.length === 0) {
|
||||
return node
|
||||
}
|
||||
|
||||
matches.sort((a, b) => a.start < b.start ? -1 : 1)
|
||||
|
||||
let span = document.createElement('span')
|
||||
let position = 0
|
||||
matches.forEach((match) => {
|
||||
if (match.start < position) {
|
||||
return
|
||||
}
|
||||
if (match.start > position) {
|
||||
span.appendChild(document.createTextNode(node.textContent.slice(position, match.start)))
|
||||
}
|
||||
|
||||
let a = document.createElement('a')
|
||||
a.textContent = match.text
|
||||
a.addEventListener('click', () => {
|
||||
match.handler.handle(match.uri)
|
||||
})
|
||||
span.appendChild(a)
|
||||
|
||||
position = match.end
|
||||
})
|
||||
span.appendChild(document.createTextNode(node.textContent.slice(position)))
|
||||
return span
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
import * as fs from 'fs'
|
||||
const untildify = require('untildify')
|
||||
import { Injectable } from '@angular/core'
|
||||
import { ElectronService } from 'terminus-core'
|
||||
|
||||
import { LinkHandler } from './api'
|
||||
|
||||
@Injectable()
|
||||
export class URLHandler extends LinkHandler {
|
||||
regex = 'http(s)?://[^\\s;\'"]+[^,;\\s]'
|
||||
|
||||
constructor (private electron: ElectronService) {
|
||||
super()
|
||||
}
|
||||
|
||||
handle (uri: string) {
|
||||
this.electron.shell.openExternal(uri)
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class FileHandler extends LinkHandler {
|
||||
regex = '[~/][^\\s,;\'"]+'
|
||||
|
||||
constructor (private electron: ElectronService) {
|
||||
super()
|
||||
}
|
||||
|
||||
convert (uri: string): string {
|
||||
return untildify(uri)
|
||||
}
|
||||
|
||||
verify (uri: string) {
|
||||
return fs.existsSync(uri)
|
||||
}
|
||||
|
||||
handle (uri: string) {
|
||||
this.electron.shell.openExternal('file://' + uri)
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
This plugin is based on Hyperterm Hyperlinks:
|
||||
https://github.com/zeit/hyperlinks/blob/master/index.js
|
||||
*/
|
||||
|
||||
import { NgModule } from '@angular/core'
|
||||
import { TerminalDecorator } from 'terminus-terminal'
|
||||
|
||||
import { LinkHandler } from './api'
|
||||
import { FileHandler, URLHandler } from './handlers'
|
||||
import { LinkHighlighterDecorator } from './decorator'
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
{ provide: LinkHandler, useClass: FileHandler, multi: true },
|
||||
{ provide: LinkHandler, useClass: URLHandler, multi: true },
|
||||
{ provide: TerminalDecorator, useClass: LinkHighlighterDecorator, multi: true },
|
||||
],
|
||||
})
|
||||
export default class LinkHighlighterModule { }
|
||||
|
||||
export * from './api'
|
@ -1,42 +0,0 @@
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
target: 'node',
|
||||
entry: 'src/index.ts',
|
||||
devtool: 'source-map',
|
||||
context: __dirname,
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'index.js',
|
||||
pathinfo: true,
|
||||
libraryTarget: 'umd',
|
||||
devtoolModuleFilenameTemplate: 'webpack-terminus-clickable-links:///[resource-path]',
|
||||
},
|
||||
resolve: {
|
||||
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
|
||||
extensions: ['.ts', '.js'],
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
loader: 'awesome-typescript-loader',
|
||||
query: {
|
||||
configFileName: path.resolve(__dirname, 'tsconfig.json'),
|
||||
paths: {
|
||||
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
|
||||
"*": [path.resolve(__dirname, '../app/node_modules/*')],
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
externals: [
|
||||
'fs',
|
||||
'untildify',
|
||||
/^rxjs/,
|
||||
/^@angular/,
|
||||
/^@ng-bootstrap/,
|
||||
/^terminus-/,
|
||||
]
|
||||
}
|
@ -17,7 +17,6 @@
|
||||
"@types/js-yaml": "^3.5.29",
|
||||
"@types/node": "^7.0.12",
|
||||
"@types/webpack-env": "^1.13.0",
|
||||
"angular2-toaster": "3.0.1",
|
||||
"awesome-typescript-loader": "^3.1.2",
|
||||
"bootstrap": "4.0.0-alpha.6",
|
||||
"core-js": "^2.4.1",
|
||||
|
@ -65,5 +65,4 @@ title-bar(
|
||||
[scrollable]='tab.scrollable',
|
||||
)
|
||||
|
||||
toaster-container([toasterconfig]="toasterconfig")
|
||||
ng-template(ngbModalContainer)
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Component, Inject, Input } from '@angular/core'
|
||||
import { trigger, style, animate, transition, state } from '@angular/animations'
|
||||
import { ToasterConfig } from 'angular2-toaster'
|
||||
|
||||
import { ElectronService } from '../services/electron.service'
|
||||
import { HostAppService, Platform } from '../services/hostApp.service'
|
||||
@ -41,7 +40,6 @@ import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
||||
]
|
||||
})
|
||||
export class AppRootComponent {
|
||||
toasterConfig: ToasterConfig
|
||||
Platform = Platform
|
||||
@Input() ready = false
|
||||
@Input() leftToolbarButtons: IToolbarButton[]
|
||||
@ -64,12 +62,6 @@ export class AppRootComponent {
|
||||
this.logger = log.create('main')
|
||||
this.logger.info('v', electron.app.getVersion())
|
||||
|
||||
this.toasterConfig = new ToasterConfig({
|
||||
mouseoverTimerStop: true,
|
||||
preventDuplicates: true,
|
||||
timeout: 4000,
|
||||
})
|
||||
|
||||
this.leftToolbarButtons = this.getToolbarButtons(false)
|
||||
this.rightToolbarButtons = this.getToolbarButtons(true)
|
||||
|
||||
|
@ -2,7 +2,6 @@ import { NgModule, ModuleWithProviders } from '@angular/core'
|
||||
import { BrowserModule } from '@angular/platform-browser'
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { ToasterModule } from 'angular2-toaster'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'
|
||||
|
||||
@ -55,7 +54,6 @@ const PROVIDERS = [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
FormsModule,
|
||||
ToasterModule,
|
||||
NgbModule,
|
||||
PerfectScrollbarModule.forRoot({
|
||||
suppressScrollX: true,
|
||||
|
@ -50,7 +50,7 @@ $input-bg-focus: $input-bg;
|
||||
//$input-border-focus: lighten($brand-primary, 25%);
|
||||
//$input-box-shadow-focus: $input-box-shadow, rgba($input-border-focus, .6);
|
||||
$input-color-focus: $input-color;
|
||||
$input-group-addon-bg: $input-bg;
|
||||
$input-group-addon-bg: $body-bg2;
|
||||
$input-group-addon-border-color: $input-border-color;
|
||||
|
||||
$modal-content-bg: $body-bg;
|
||||
@ -69,7 +69,8 @@ $dropdown-link-hover-bg: $body-bg3;
|
||||
$dropdown-link-disabled-color: #333;
|
||||
$dropdown-header-color: #333;
|
||||
|
||||
|
||||
$list-group-color: $body-color;
|
||||
$list-group-bg: $body-bg2;
|
||||
|
||||
@import '~bootstrap/scss/bootstrap.scss';
|
||||
|
||||
|
2649
terminus-plugin-manager/package-lock.json
generated
Normal file
2649
terminus-plugin-manager/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
46
terminus-plugin-manager/package.json
Normal file
46
terminus-plugin-manager/package.json
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "terminus-plugin-manager",
|
||||
"version": "0.0.1",
|
||||
"description": "Terminus' plugin manager",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "webpack --progress --color --display-modules",
|
||||
"watch": "webpack --progress --color --watch"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/mz": "0.0.31",
|
||||
"@types/node": "7.0.12",
|
||||
"@types/npm": "^2.0.28",
|
||||
"@types/semver": "^5.3.31",
|
||||
"@types/webpack-env": "1.13.0",
|
||||
"awesome-typescript-loader": "3.1.2",
|
||||
"css-loader": "^0.28.0",
|
||||
"pug": "^2.0.0-beta11",
|
||||
"pug-loader": "^2.3.0",
|
||||
"raw-loader": "^0.5.1",
|
||||
"sass-loader": "^6.0.3",
|
||||
"semver": "^5.3.0",
|
||||
"to-string-loader": "^1.1.5",
|
||||
"typescript": "^2.2.2",
|
||||
"webpack": "2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "4.0.1",
|
||||
"@angular/core": "4.0.1",
|
||||
"@angular/forms": "4.0.1",
|
||||
"@angular/platform-browser": "4.0.1",
|
||||
"@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.22",
|
||||
"terminus-core": "*",
|
||||
"terminus-settings": "*",
|
||||
"rxjs": "5.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.16.1"
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
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}}
|
||||
p.mb-0.mr-3 {{plugin.version}}
|
||||
button.btn.btn-outline-primary((click)='upgradePlugin(plugin)')
|
||||
i.fa.fa-arrow-up
|
||||
span Upgrade ({{knownUpgrades[plugin.name].version}})
|
||||
small.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}}
|
||||
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}}
|
||||
|
||||
|
||||
h3.mt-4 Available
|
||||
|
||||
.input-group.mb-4
|
||||
.input-group-addon
|
||||
i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='!availablePluginsReady')
|
||||
i.fa.fa-fw.fa-search(*ngIf='availablePluginsReady')
|
||||
input.form-control(
|
||||
type='text',
|
||||
'[(ngModel)]'='_1',
|
||||
(ngModelChange)='availablePluginsQuery$.next(_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}}
|
||||
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}}
|
@ -0,0 +1,8 @@
|
||||
.appearance-preview {
|
||||
padding: 10px 20px;
|
||||
margin: 0 0 10px;
|
||||
overflow: hidden;
|
||||
span {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
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 { IPluginInfo, PluginManagerService } from '../services/pluginManager.service'
|
||||
|
||||
@Component({
|
||||
template: require('./pluginsSettingsTab.component.pug'),
|
||||
styles: [require('./pluginsSettingsTab.component.scss')],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PluginsSettingsTabComponent {
|
||||
availablePlugins$: Observable<IPluginInfo[]>
|
||||
availablePluginsQuery$ = new BehaviorSubject<string>('')
|
||||
availablePluginsReady = false
|
||||
knownUpgrades: {[id: string]: IPluginInfo} = {}
|
||||
busy: boolean
|
||||
|
||||
constructor (
|
||||
public pluginManager: PluginManagerService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.availablePlugins$ = this.availablePluginsQuery$
|
||||
.debounceTime(200)
|
||||
.distinctUntilChanged()
|
||||
.flatMap(query => {
|
||||
this.availablePluginsReady = false
|
||||
return this.pluginManager.listAvailable(query).do(() => {
|
||||
this.availablePluginsReady = true
|
||||
})
|
||||
})
|
||||
this.availablePlugins$.first().subscribe(available => {
|
||||
for (let plugin of this.pluginManager.installedPlugins) {
|
||||
this.knownUpgrades[plugin.name] = available.find(x => x.name === plugin.name && semver.gt(x.version, plugin.version))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
isAlreadyInstalled (plugin: IPluginInfo): boolean {
|
||||
return this.pluginManager.installedPlugins.some(x => x.name === plugin.name)
|
||||
}
|
||||
|
||||
async installPlugin (plugin: IPluginInfo): Promise<void> {
|
||||
this.busy = true
|
||||
}
|
||||
|
||||
async upgradePlugin (plugin: IPluginInfo): Promise<void> {
|
||||
return this.installPlugin(this.knownUpgrades[plugin.name])
|
||||
}
|
||||
}
|
31
terminus-plugin-manager/src/index.ts
Normal file
31
terminus-plugin-manager/src/index.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { BrowserModule } from '@angular/platform-browser'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
import { SettingsTabProvider } from 'terminus-settings'
|
||||
|
||||
import { PluginsSettingsTabComponent } from './components/pluginsSettingsTab.component'
|
||||
import { PluginManagerService } from './services/pluginManager.service'
|
||||
import { PluginsSettingsTabProvider } from './settings'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
NgbModule,
|
||||
],
|
||||
providers: [
|
||||
{ provide: SettingsTabProvider, useClass: PluginsSettingsTabProvider, multi: true },
|
||||
PluginManagerService,
|
||||
],
|
||||
entryComponents: [
|
||||
PluginsSettingsTabComponent,
|
||||
],
|
||||
declarations: [
|
||||
PluginsSettingsTabComponent,
|
||||
],
|
||||
})
|
||||
export default class PluginManagerModule { }
|
||||
|
||||
export { PluginManagerService }
|
@ -0,0 +1,49 @@
|
||||
import * as fs from 'fs-promise'
|
||||
import { Observable } from 'rxjs'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Logger, LogService } from 'terminus-core'
|
||||
import axios from 'axios'
|
||||
|
||||
const NAME_PREFIX = 'terminus-'
|
||||
|
||||
export interface IPluginInfo {
|
||||
name: string
|
||||
description: string
|
||||
packageName: string
|
||||
isBuiltin: boolean
|
||||
version: string
|
||||
homepage?: string
|
||||
path?: string
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PluginManagerService {
|
||||
logger: Logger
|
||||
builtinPluginsPath: string = (window as any).builtinPluginsPath
|
||||
userPluginsPath: string = (window as any).userPluginsPath
|
||||
installedPlugins: IPluginInfo[] = (window as any).installedPlugins
|
||||
|
||||
constructor (
|
||||
log: LogService,
|
||||
) {
|
||||
this.logger = log.create('pluginManager')
|
||||
}
|
||||
|
||||
listAvailable (query?: string): Observable<IPluginInfo[]> {
|
||||
return Observable
|
||||
.fromPromise(
|
||||
axios.get(`https://www.npmjs.com/-/search?text=${NAME_PREFIX}+${encodeURIComponent(query || '')}&from=0&size=1000`)
|
||||
)
|
||||
.map(response => response.data.objects.map(item => ({
|
||||
name: item.package.name.substring(NAME_PREFIX.length),
|
||||
packageName: item.package.name,
|
||||
description: item.package.description,
|
||||
version: item.package.version,
|
||||
homepage: item.package.links.homepage,
|
||||
})))
|
||||
.map(plugins => plugins.filter(x => x.packageName.startsWith(NAME_PREFIX)))
|
||||
}
|
||||
|
||||
async installPlugin (plugin: IPluginInfo) {
|
||||
}
|
||||
}
|
13
terminus-plugin-manager/src/settings.ts
Normal file
13
terminus-plugin-manager/src/settings.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { SettingsTabProvider, ComponentType } from 'terminus-settings'
|
||||
|
||||
import { PluginsSettingsTabComponent } from './components/pluginsSettingsTab.component'
|
||||
|
||||
@Injectable()
|
||||
export class PluginsSettingsTabProvider extends SettingsTabProvider {
|
||||
title = 'Plugins'
|
||||
|
||||
getComponentType (): ComponentType {
|
||||
return PluginsSettingsTabComponent
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ module.exports = {
|
||||
filename: 'index.js',
|
||||
pathinfo: true,
|
||||
libraryTarget: 'umd',
|
||||
devtoolModuleFilenameTemplate: 'webpack-terminus-plugin-manager:///[resource-path]',
|
||||
},
|
||||
resolve: {
|
||||
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
|
||||
@ -20,7 +21,7 @@ module.exports = {
|
||||
{
|
||||
test: /\.ts$/,
|
||||
loader: 'awesome-typescript-loader',
|
||||
options: {
|
||||
query: {
|
||||
configFileName: path.resolve(__dirname, 'tsconfig.json'),
|
||||
paths: {
|
||||
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
|
||||
@ -28,10 +29,19 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
|
||||
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
|
||||
]
|
||||
},
|
||||
externals: [
|
||||
'fs',
|
||||
'fs-promise',
|
||||
'font-manager',
|
||||
'path',
|
||||
'node-pty',
|
||||
'mz/child_process',
|
||||
'winreg',
|
||||
/^rxjs/,
|
||||
/^@angular/,
|
||||
/^@ng-bootstrap/,
|
||||
/^terminus-/,
|
@ -74,8 +74,10 @@ export class TerminalTabComponent extends BaseTabComponent {
|
||||
|
||||
ngOnInit () {
|
||||
this.focused$.subscribe(() => {
|
||||
this.hterm.scrollPort_.focus()
|
||||
this.hterm.scrollPort_.resize()
|
||||
setTimeout(() => {
|
||||
this.hterm.scrollPort_.resize()
|
||||
this.hterm.scrollPort_.focus()
|
||||
}, 100)
|
||||
})
|
||||
|
||||
this.hterm = new hterm.hterm.Terminal()
|
||||
@ -88,6 +90,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
||||
this.hterm.onTerminalReady = () => {
|
||||
this.htermVisible = true
|
||||
this.hterm.installKeyboard()
|
||||
this.hterm.scrollPort_.setCtrlVPaste(true)
|
||||
this.io = this.hterm.io.push()
|
||||
this.attachIOHandlers(this.io)
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "terminus-theme-hype",
|
||||
"version": "0.0.1",
|
||||
"description": "Hyper inspired theme for Terminus",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "webpack --progress --color",
|
||||
"watch": "webpack --progress --color --watch"
|
||||
},
|
||||
"files": ["dist"],
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@angular/core": "4.0.1",
|
||||
"terminus-core": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/webpack-env": "1.13.0",
|
||||
"node-sass": "^4.5.0",
|
||||
"bootstrap": "4.0.0-alpha.6",
|
||||
"sass-loader": "^6.0.3",
|
||||
"css-loader": "0.26.1",
|
||||
"typescript": "~2.1.0",
|
||||
"to-string-loader": "^1.1.5",
|
||||
"awesome-typescript-loader": "3.1.2",
|
||||
"raw-loader": "0.5.1",
|
||||
"webpack": "2.3.3"
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import { NgModule, Injectable } from '@angular/core'
|
||||
import { Theme } from 'terminus-core'
|
||||
|
||||
@Injectable()
|
||||
class HypeTheme extends Theme {
|
||||
name = 'Hype'
|
||||
css = require('./theme.scss')
|
||||
terminalBackground = '#010101'
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
{ provide: Theme, useClass: HypeTheme, multi: true },
|
||||
],
|
||||
})
|
||||
export default class HypeThemeModule { }
|
@ -1,258 +0,0 @@
|
||||
$window-border: #313131;
|
||||
$body-bg: #010101;
|
||||
$body-bg2: #010101;
|
||||
$body-bg3: #111;
|
||||
|
||||
|
||||
$tab-border-radius: 3px;
|
||||
$button-hover-bg: rgba(255, 255, 255, .05);
|
||||
$button-active-bg: rgba(255, 255, 255, .1);
|
||||
|
||||
|
||||
$white: #fff !default;
|
||||
$black: #000 !default;
|
||||
$red: #d9534f !default;
|
||||
$orange: #f0ad4e !default;
|
||||
$yellow: #ffd500 !default;
|
||||
$green: #5cb85c !default;
|
||||
$blue: #0275d8 !default;
|
||||
$teal: #5bc0de !default;
|
||||
$pink: #ff5b77 !default;
|
||||
$purple: #613d7c !default;
|
||||
|
||||
|
||||
$body-color: #aaa;
|
||||
$font-family-sans-serif: "Source Sans Pro";
|
||||
$font-size-base: 14rem / 16;
|
||||
|
||||
$btn-secondary-color: #ccc;
|
||||
$btn-secondary-bg: #222;
|
||||
$btn-secondary-border: #444;
|
||||
|
||||
//$btn-warning-bg: rgba($orange, .5);
|
||||
|
||||
|
||||
$nav-tabs-border-color: $body-bg2;
|
||||
$nav-tabs-border-width: 1px;
|
||||
$nav-tabs-border-radius: 0;
|
||||
$nav-tabs-link-hover-border-color: $body-bg2;
|
||||
$nav-tabs-active-link-hover-color: $white;
|
||||
$nav-tabs-active-link-hover-bg: $blue;
|
||||
$nav-tabs-active-link-hover-border-color: darken($blue, 30%);
|
||||
|
||||
$input-bg: #111;
|
||||
$input-bg-disabled: #333;
|
||||
|
||||
$input-color: $body-color;
|
||||
//$input-border-color: rgba($black,.15);
|
||||
//$input-box-shadow: inset 0 1px 1px rgba($black,.075);
|
||||
|
||||
$input-border-radius: 0;
|
||||
|
||||
$input-bg-focus: $input-bg;
|
||||
//$input-border-focus: lighten($brand-primary, 25%);
|
||||
//$input-box-shadow-focus: $input-box-shadow, rgba($input-border-focus, .6);
|
||||
$input-color-focus: $input-color;
|
||||
|
||||
$modal-content-bg: $body-bg;
|
||||
$modal-content-border-color: $body-bg2;
|
||||
$modal-header-border-color: $body-bg2;
|
||||
$modal-footer-border-color: $body-bg2;
|
||||
|
||||
$popover-bg: $body-bg2;
|
||||
|
||||
|
||||
@import '~bootstrap/scss/bootstrap.scss';
|
||||
|
||||
.nav-tabs {
|
||||
background: $btn-secondary-bg;
|
||||
.nav-link {
|
||||
transition: 0.25s all;
|
||||
border-bottom-color: $nav-tabs-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
ngb-tabset .tab-content {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
[ngbradiogroup] > label.active {
|
||||
background: $blue;
|
||||
}
|
||||
|
||||
title-bar {
|
||||
background: $body-bg2;
|
||||
|
||||
button {
|
||||
&:hover { background: $button-hover-bg !important; }
|
||||
&:active { background: $button-active-bg !important; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
app-root {
|
||||
background: $body-bg;
|
||||
border: 1px solid $window-border;
|
||||
|
||||
&> .content {
|
||||
.tab-bar {
|
||||
.btn-tab-bar {
|
||||
background: transparent;
|
||||
&:hover { background: $button-hover-bg !important; }
|
||||
&:active { background: $button-active-bg !important; }
|
||||
}
|
||||
|
||||
&>.tabs {
|
||||
tab-header {
|
||||
background: $body-bg;
|
||||
|
||||
.index {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.name {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
color: $body-color;
|
||||
border: none;
|
||||
transition: 0.25s all;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.tabs-on-top .tab-bar {
|
||||
margin-top: -1px;
|
||||
border-bottom: 1px solid $window-border;
|
||||
|
||||
tab-header {
|
||||
margin-bottom: -1px;
|
||||
border: 1px solid transparent;
|
||||
border-bottom: 1px solid $window-border;
|
||||
border-top-left-radius: $tab-border-radius;
|
||||
border-top-right-radius: $tab-border-radius;
|
||||
|
||||
&.active {
|
||||
border: 1px solid $window-border;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
&.has-activity:not(.active) {
|
||||
border-top: 1px solid $green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.tabs-on-top) .tab-bar {
|
||||
margin-bottom: -1px;
|
||||
border-top: 1px solid $window-border;
|
||||
|
||||
tab-header {
|
||||
margin-top: -1px;
|
||||
border: 1px solid transparent;
|
||||
border-top: 1px solid $window-border;
|
||||
border-bottom-left-radius: $tab-border-radius;
|
||||
border-bottom-right-radius: $tab-border-radius;
|
||||
|
||||
&.active {
|
||||
border: 1px solid $window-border;
|
||||
border-top: 1px solid transparent;
|
||||
}
|
||||
|
||||
&.has-activity:not(.active) {
|
||||
border-bottom: 1px solid $green;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tab-body {
|
||||
background: $body-bg;
|
||||
}
|
||||
|
||||
settings-tab > ngb-tabset {
|
||||
border-right: 1px solid $body-bg2;
|
||||
|
||||
& > .nav {
|
||||
background: $body-bg3;
|
||||
|
||||
& > .nav-item > .nav-link {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
padding: 10px 50px 10px 20px;
|
||||
font-size: 14px;
|
||||
|
||||
&.active {
|
||||
border-top-color: $nav-tabs-active-link-hover-border-color;
|
||||
border-bottom-color: $nav-tabs-active-link-hover-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
multi-hotkey-input {
|
||||
.item {
|
||||
background: $body-bg3;
|
||||
border: 1px solid $blue;
|
||||
border-radius: 3px;
|
||||
margin-right: 5px;
|
||||
|
||||
.body {
|
||||
padding: 3px 0 2px;
|
||||
|
||||
.stroke {
|
||||
padding: 0 6px;
|
||||
border-right: 1px solid $body-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.remove {
|
||||
padding: 3px 8px 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.add {
|
||||
color: #777;
|
||||
padding: 4px 10px 0;
|
||||
}
|
||||
|
||||
.add, .item .body, .item .remove {
|
||||
&:hover { background: darken($body-bg3, 5%); }
|
||||
&:active { background: darken($body-bg3, 15%); }
|
||||
}
|
||||
}
|
||||
|
||||
hotkey-input-modal {
|
||||
.input {
|
||||
background: $input-bg;
|
||||
padding: 10px;
|
||||
font-size: 24px;
|
||||
line-height: 27px;
|
||||
height: 55px;
|
||||
|
||||
.stroke {
|
||||
background: $body-bg3;
|
||||
border: 1px solid $blue;
|
||||
border-radius: 3px;
|
||||
margin-right: 10px;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.timeout {
|
||||
background: $input-bg;
|
||||
|
||||
div {
|
||||
background: $blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
margin-bottom: 2px;
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"module": "commonjs",
|
||||
"target": "es2016",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015",
|
||||
"es7"
|
||||
]
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
@ -3,8 +3,6 @@ module.exports = [
|
||||
require('./terminus-core/webpack.config.js'),
|
||||
require('./terminus-settings/webpack.config.js'),
|
||||
require('./terminus-terminal/webpack.config.js'),
|
||||
require('./terminus-clickable-links/webpack.config.js'),
|
||||
require('./terminus-community-color-schemes/webpack.config.js'),
|
||||
//require('./terminus-plugin-manager/webpack.config.js'),
|
||||
require('./terminus-theme-hype/webpack.config.js'),
|
||||
require('./terminus-plugin-manager/webpack.config.js'),
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user