removed temp files
Before Width: | Height: | Size: 4.9 KiB |
@ -1,55 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:url(#SVGID_1_);}
|
||||
.st1{opacity:0.16;fill:url(#SVGID_2_);}
|
||||
.st2{fill:url(#SVGID_3_);}
|
||||
.st3{opacity:0.16;fill:url(#SVGID_4_);}
|
||||
.st4{fill:url(#SVGID_5_);}
|
||||
.st5{opacity:0.15;fill:url(#SVGID_6_);}
|
||||
.st6{fill:url(#SVGID_7_);}
|
||||
</style>
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="260.9675" y1="871.1813" x2="919.1845" y2="491.1596">
|
||||
<stop offset="0" style="stop-color:#669ABD"/>
|
||||
<stop offset="1" style="stop-color:#77DBDB"/>
|
||||
</linearGradient>
|
||||
<polygon class="st0" points="297.54,934.52 882.6,596.72 882.61,427.82 297.54,765.65 "/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="553.5051" y1="617.8278" x2="626.647" y2="744.5132">
|
||||
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
|
||||
<stop offset="1" style="stop-color:#000000"/>
|
||||
</linearGradient>
|
||||
<polygon class="st1" points="297.54,934.52 882.6,596.72 882.61,427.82 297.54,765.65 "/>
|
||||
</g>
|
||||
<g>
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="114.6631" y1="744.5275" x2="334.0905" y2="871.2141">
|
||||
<stop offset="0" style="stop-color:#6A8FAD"/>
|
||||
<stop offset="1" style="stop-color:#669ABD"/>
|
||||
</linearGradient>
|
||||
<polygon class="st2" points="151.23,681.18 151.22,850.09 297.54,934.52 297.54,765.65 "/>
|
||||
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="260.9478" y1="744.5281" x2="187.8059" y2="871.2135">
|
||||
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
|
||||
<stop offset="1" style="stop-color:#000000"/>
|
||||
</linearGradient>
|
||||
<polygon class="st3" points="151.23,681.18 151.22,850.09 297.54,934.52 297.54,765.65 "/>
|
||||
</g>
|
||||
<g>
|
||||
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="114.663" y1="237.793" x2="553.5026" y2="491.1571">
|
||||
<stop offset="0" style="stop-color:#6A8FAD"/>
|
||||
<stop offset="1" style="stop-color:#669ABD"/>
|
||||
</linearGradient>
|
||||
<polygon class="st4" points="151.23,174.45 151.21,343.36 443.79,512.27 590.08,427.81 "/>
|
||||
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="370.6562" y1="301.1281" x2="297.5094" y2="427.8221">
|
||||
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
|
||||
<stop offset="1" style="stop-color:#000000"/>
|
||||
</linearGradient>
|
||||
<polygon class="st5" points="151.23,174.45 151.21,343.36 443.79,512.27 590.08,427.81 "/>
|
||||
</g>
|
||||
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="78.0912" y1="554.4979" x2="736.3375" y2="174.4593">
|
||||
<stop offset="0" style="stop-color:#CCECFF"/>
|
||||
<stop offset="1" style="stop-color:#9FECED"/>
|
||||
</linearGradient>
|
||||
<polygon class="st6" points="297.51,765.64 151.23,681.18 590.08,427.81 151.23,174.45 297.5,90 882.61,427.82 "/>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 415 B |
Before Width: | Height: | Size: 955 B |
Before Width: | Height: | Size: 365 B |
Before Width: | Height: | Size: 894 B |
Before Width: | Height: | Size: 1.4 KiB |
@ -1,4 +0,0 @@
|
||||
owner: eugeny
|
||||
repo: terminus
|
||||
provider: github
|
||||
updaterCacheDirName: terminus-updater
|
@ -1,22 +0,0 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
meta(charset='UTF-8')
|
||||
base(href='index.html')
|
||||
script.
|
||||
console.timeStamp('index')
|
||||
window.nodeRequire = require
|
||||
script(src='./preload.js')
|
||||
script(src='./bundle.js', defer)
|
||||
style.
|
||||
body { transition: 0.5s background; }
|
||||
body
|
||||
style#custom-css
|
||||
app-root
|
||||
.preload-logo
|
||||
div
|
||||
.terminus-logo
|
||||
h1.terminus-title Terminus
|
||||
sup α
|
||||
.progress
|
||||
.bar(style='width: 0%')
|
230
tmp/lib/app.ts
@ -1,230 +0,0 @@
|
||||
import { app, ipcMain, Menu, Tray, shell, screen, globalShortcut, MenuItemConstructorOptions } from 'electron'
|
||||
import { loadConfig } from './config'
|
||||
import { Window, WindowOptions } from './window'
|
||||
|
||||
export class Application {
|
||||
private tray: Tray
|
||||
private windows: Window[] = []
|
||||
|
||||
constructor () {
|
||||
ipcMain.on('app:config-change', (_event, config) => {
|
||||
this.broadcast('host:config-change', config)
|
||||
})
|
||||
|
||||
ipcMain.on('app:register-global-hotkey', (_event, specs) => {
|
||||
globalShortcut.unregisterAll()
|
||||
for (const spec of specs) {
|
||||
globalShortcut.register(spec, () => {
|
||||
this.onGlobalHotkey()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const configData = loadConfig()
|
||||
if (process.platform === 'linux') {
|
||||
app.commandLine.appendSwitch('no-sandbox')
|
||||
if (((configData.appearance || {}).opacity || 1) !== 1) {
|
||||
app.commandLine.appendSwitch('enable-transparent-visuals')
|
||||
app.disableHardwareAcceleration()
|
||||
}
|
||||
}
|
||||
|
||||
app.commandLine.appendSwitch('disable-http-cache')
|
||||
app.commandLine.appendSwitch('lang', 'EN')
|
||||
app.allowRendererProcessReuse = false
|
||||
|
||||
for (const flag of configData.flags || [['force_discrete_gpu', '0']]) {
|
||||
app.commandLine.appendSwitch(flag[0], flag[1])
|
||||
}
|
||||
}
|
||||
|
||||
init (): void {
|
||||
screen.on('display-metrics-changed', () => this.broadcast('host:display-metrics-changed'))
|
||||
screen.on('display-added', () => this.broadcast('host:displays-changed'))
|
||||
screen.on('display-removed', () => this.broadcast('host:displays-changed'))
|
||||
}
|
||||
|
||||
async newWindow (options?: WindowOptions): Promise<Window> {
|
||||
const window = new Window(options)
|
||||
this.windows.push(window)
|
||||
window.visible$.subscribe(visible => {
|
||||
if (visible) {
|
||||
this.disableTray()
|
||||
} else {
|
||||
this.enableTray()
|
||||
}
|
||||
})
|
||||
window.closed$.subscribe(() => {
|
||||
this.windows = this.windows.filter(x => x !== window)
|
||||
})
|
||||
if (process.platform === 'darwin') {
|
||||
this.setupMenu()
|
||||
}
|
||||
await window.ready
|
||||
return window
|
||||
}
|
||||
|
||||
onGlobalHotkey (): void {
|
||||
if (this.windows.some(x => x.isFocused())) {
|
||||
for (const window of this.windows) {
|
||||
window.hide()
|
||||
}
|
||||
} else {
|
||||
for (const window of this.windows) {
|
||||
window.present()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
presentAllWindows (): void {
|
||||
for (const window of this.windows) {
|
||||
window.present()
|
||||
}
|
||||
}
|
||||
|
||||
broadcast (event: string, ...args: any[]): void {
|
||||
for (const window of this.windows) {
|
||||
window.send(event, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
async send (event: string, ...args: any[]): Promise<void> {
|
||||
if (!this.hasWindows()) {
|
||||
await this.newWindow()
|
||||
}
|
||||
this.windows.filter(w => !w.isDestroyed())[0].send(event, ...args)
|
||||
}
|
||||
|
||||
enableTray (): void {
|
||||
if (this.tray) {
|
||||
return
|
||||
}
|
||||
if (process.platform === 'darwin') {
|
||||
this.tray = new Tray(`${app.getAppPath()}/assets/tray-darwinTemplate.png`)
|
||||
this.tray.setPressedImage(`${app.getAppPath()}/assets/tray-darwinHighlightTemplate.png`)
|
||||
} else {
|
||||
this.tray = new Tray(`${app.getAppPath()}/assets/tray.png`)
|
||||
}
|
||||
|
||||
this.tray.on('click', () => setTimeout(() => this.focus()))
|
||||
|
||||
const contextMenu = Menu.buildFromTemplate([{
|
||||
label: 'Show',
|
||||
click: () => this.focus(),
|
||||
}])
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
this.tray.setContextMenu(contextMenu)
|
||||
}
|
||||
|
||||
this.tray.setToolTip(`Terminus ${app.getVersion()}`)
|
||||
}
|
||||
|
||||
disableTray (): void {
|
||||
if (this.tray) {
|
||||
this.tray.destroy()
|
||||
this.tray = null
|
||||
}
|
||||
}
|
||||
|
||||
hasWindows (): boolean {
|
||||
return !!this.windows.length
|
||||
}
|
||||
|
||||
focus (): void {
|
||||
for (const window of this.windows) {
|
||||
window.show()
|
||||
}
|
||||
}
|
||||
|
||||
handleSecondInstance (argv: string[], cwd: string): void {
|
||||
this.presentAllWindows()
|
||||
this.windows[this.windows.length - 1].handleSecondInstance(argv, cwd)
|
||||
}
|
||||
|
||||
private setupMenu () {
|
||||
const template: MenuItemConstructorOptions[] = [
|
||||
{
|
||||
label: 'Application',
|
||||
submenu: [
|
||||
{ role: 'about', label: 'About Terminus' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Preferences',
|
||||
accelerator: 'Cmd+,',
|
||||
click: async () => {
|
||||
if (!this.hasWindows()) {
|
||||
await this.newWindow()
|
||||
}
|
||||
this.windows[0].send('host:preferences-menu')
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ role: 'services', submenu: [] },
|
||||
{ type: 'separator' },
|
||||
{ role: 'hide' },
|
||||
{ role: 'hideOthers' },
|
||||
{ role: 'unhide' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Quit',
|
||||
accelerator: 'Cmd+Q',
|
||||
click () {
|
||||
app.quit()
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{ role: 'undo' },
|
||||
{ role: 'redo' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'cut' },
|
||||
{ role: 'copy' },
|
||||
{ role: 'paste' },
|
||||
{ role: 'pasteAndMatchStyle' },
|
||||
{ role: 'delete' },
|
||||
{ role: 'selectAll' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{ role: 'reload' },
|
||||
{ role: 'forceReload' },
|
||||
{ role: 'toggleDevTools' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'resetZoom' },
|
||||
{ role: 'zoomIn' },
|
||||
{ role: 'zoomOut' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'togglefullscreen' },
|
||||
],
|
||||
},
|
||||
{
|
||||
role: 'window',
|
||||
submenu: [
|
||||
{ role: 'minimize' },
|
||||
{ role: 'zoom' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'front' },
|
||||
],
|
||||
},
|
||||
{
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Website',
|
||||
click () {
|
||||
shell.openExternal('https://eugeny.github.io/terminus')
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
import { app } from 'electron'
|
||||
|
||||
export function parseArgs (argv: string[], cwd: string): any {
|
||||
if (argv[0].includes('node')) {
|
||||
argv = argv.slice(1)
|
||||
}
|
||||
|
||||
return require('yargs')
|
||||
.usage('terminus [command] [arguments]')
|
||||
.command('open [directory]', 'open a shell in a directory', {
|
||||
directory: { type: 'string', 'default': cwd },
|
||||
})
|
||||
.command('run [command...]', 'run a command in the terminal', {
|
||||
command: { type: 'string' },
|
||||
})
|
||||
.command('profile [profileName]', 'open a tab with specified profile', {
|
||||
profileName: { type: 'string' },
|
||||
})
|
||||
.command('paste [text]', 'paste stdin into the active tab', yargs => {
|
||||
return yargs.option('escape', {
|
||||
alias: 'e',
|
||||
type: 'boolean',
|
||||
describe: 'Perform shell escaping',
|
||||
}).positional('text', {
|
||||
type: 'string',
|
||||
})
|
||||
})
|
||||
.version('version', '', app.getVersion())
|
||||
.option('debug', {
|
||||
alias: 'd',
|
||||
describe: 'Show DevTools on start',
|
||||
type: 'boolean',
|
||||
})
|
||||
.option('hidden', {
|
||||
describe: 'Start minimized',
|
||||
type: 'boolean',
|
||||
})
|
||||
.option('version', {
|
||||
alias: 'v',
|
||||
describe: 'Show version and exit',
|
||||
type: 'boolean',
|
||||
})
|
||||
.help('help')
|
||||
.parse(argv.slice(1))
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as yaml from 'js-yaml'
|
||||
import { app } from 'electron'
|
||||
|
||||
export function loadConfig (): any {
|
||||
const configPath = path.join(app.getPath('userData'), 'config.yaml')
|
||||
if (fs.existsSync(configPath)) {
|
||||
return yaml.safeLoad(fs.readFileSync(configPath, 'utf8'))
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import './portable'
|
||||
import './sentry'
|
||||
import './lru'
|
||||
import { app, ipcMain, Menu } from 'electron'
|
||||
import { parseArgs } from './cli'
|
||||
import { Application } from './app'
|
||||
import electronDebug = require('electron-debug')
|
||||
|
||||
if (!process.env.TERMINUS_PLUGINS) {
|
||||
process.env.TERMINUS_PLUGINS = ''
|
||||
}
|
||||
|
||||
const application = new Application()
|
||||
|
||||
ipcMain.on('app:new-window', () => {
|
||||
application.newWindow()
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (!application.hasWindows()) {
|
||||
application.newWindow()
|
||||
} else {
|
||||
application.focus()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
app.quit()
|
||||
})
|
||||
|
||||
process.on('uncaughtException' as any, err => {
|
||||
console.log(err)
|
||||
application.broadcast('uncaughtException', err)
|
||||
})
|
||||
|
||||
app.on('second-instance', (_event, argv, cwd) => {
|
||||
application.handleSecondInstance(argv, cwd)
|
||||
})
|
||||
|
||||
const argv = parseArgs(process.argv, process.cwd())
|
||||
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.quit()
|
||||
app.exit(0)
|
||||
}
|
||||
|
||||
if (argv.d) {
|
||||
electronDebug({
|
||||
isEnabled: true,
|
||||
showDevTools: true,
|
||||
devToolsMode: 'undocked',
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
if (process.platform === 'darwin') {
|
||||
app.dock.setMenu(Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'New window',
|
||||
click () {
|
||||
this.app.newWindow()
|
||||
},
|
||||
},
|
||||
]))
|
||||
}
|
||||
application.init()
|
||||
application.newWindow({ hidden: argv.hidden })
|
||||
})
|
@ -1,17 +0,0 @@
|
||||
import * as LRU from 'lru-cache'
|
||||
import * as fs from 'fs'
|
||||
const lru = new LRU({ max: 256, maxAge: 250 })
|
||||
const origLstat = fs.realpathSync.bind(fs)
|
||||
|
||||
// NB: The biggest offender of thrashing realpathSync is the node module system
|
||||
// itself, which we can't get into via any sane means.
|
||||
require('fs').realpathSync = function (p) {
|
||||
let r = lru.get(p)
|
||||
if (r) {
|
||||
return r
|
||||
}
|
||||
|
||||
r = origLstat(p)
|
||||
lru.set(p, r)
|
||||
return r
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
|
||||
let appPath: string | null = null
|
||||
try {
|
||||
appPath = path.dirname(require('electron').app.getPath('exe'))
|
||||
} catch {
|
||||
appPath = path.dirname(require('electron').remote.app.getPath('exe'))
|
||||
}
|
||||
|
||||
if (null != appPath) {
|
||||
if (fs.existsSync(path.join(appPath, 'terminus-data'))) {
|
||||
fs.renameSync(path.join(appPath, 'terminus-data'), path.join(appPath, 'data'))
|
||||
}
|
||||
const portableData = path.join(appPath, 'data')
|
||||
if (fs.existsSync(portableData)) {
|
||||
console.log('reset user data to ' + portableData)
|
||||
try {
|
||||
require('electron').app.setPath('userData', portableData)
|
||||
} catch {
|
||||
require('electron').remote.app.setPath('userData', portableData)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
const { init } = String(process.type) === 'main' ? require('@sentry/electron/dist/main') : require('@sentry/electron/dist/renderer')
|
||||
import * as isDev from 'electron-is-dev'
|
||||
|
||||
|
||||
const SENTRY_DSN = 'https://4717a0a7ee0b4429bd3a0f06c3d7eec3@sentry.io/181876'
|
||||
let release = null
|
||||
try {
|
||||
release = require('electron').app.getVersion()
|
||||
} catch {
|
||||
release = require('electron').remote.app.getVersion()
|
||||
}
|
||||
|
||||
if (!isDev) {
|
||||
init({
|
||||
dsn: SENTRY_DSN,
|
||||
release,
|
||||
integrations (integrations) {
|
||||
return integrations.filter(integration => integration.name !== 'Breadcrumbs')
|
||||
},
|
||||
})
|
||||
}
|
@ -1,389 +0,0 @@
|
||||
import * as glasstron from 'glasstron'
|
||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||
glasstron.init()
|
||||
}
|
||||
|
||||
import { Subject, Observable } from 'rxjs'
|
||||
import { debounceTime } from 'rxjs/operators'
|
||||
import { BrowserWindow, app, ipcMain, Rectangle, Menu, screen, BrowserWindowConstructorOptions } from 'electron'
|
||||
import ElectronConfig = require('electron-config')
|
||||
import * as os from 'os'
|
||||
import * as path from 'path'
|
||||
|
||||
import { parseArgs } from './cli'
|
||||
import { loadConfig } from './config'
|
||||
|
||||
let DwmEnableBlurBehindWindow: any = null
|
||||
if (process.platform === 'win32') {
|
||||
DwmEnableBlurBehindWindow = require('windows-blurbehind').DwmEnableBlurBehindWindow
|
||||
}
|
||||
|
||||
export interface WindowOptions {
|
||||
hidden?: boolean
|
||||
}
|
||||
|
||||
export class Window {
|
||||
ready: Promise<void>
|
||||
private visible = new Subject<boolean>()
|
||||
private closed = new Subject<void>()
|
||||
private window: BrowserWindow
|
||||
private windowConfig: ElectronConfig
|
||||
private windowBounds: Rectangle
|
||||
private closing = false
|
||||
private lastVibrancy: {enabled: boolean, type?: string} | null = null
|
||||
private disableVibrancyWhileDragging = false
|
||||
private configStore: any
|
||||
|
||||
get visible$ (): Observable<boolean> { return this.visible }
|
||||
get closed$ (): Observable<void> { return this.closed }
|
||||
|
||||
constructor (options?: WindowOptions) {
|
||||
this.configStore = loadConfig()
|
||||
|
||||
options = options || {}
|
||||
|
||||
this.windowConfig = new ElectronConfig({ name: 'window' })
|
||||
this.windowBounds = this.windowConfig.get('windowBoundaries')
|
||||
|
||||
const maximized = this.windowConfig.get('maximized')
|
||||
const bwOptions: BrowserWindowConstructorOptions = {
|
||||
width: 800,
|
||||
height: 600,
|
||||
title: 'Terminus',
|
||||
minWidth: 400,
|
||||
minHeight: 300,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
preload: path.join(__dirname, 'sentry.js'),
|
||||
backgroundThrottling: false,
|
||||
enableRemoteModule: true,
|
||||
},
|
||||
frame: false,
|
||||
show: false,
|
||||
backgroundColor: '#00000000',
|
||||
}
|
||||
|
||||
if (this.windowBounds) {
|
||||
Object.assign(bwOptions, this.windowBounds)
|
||||
const closestDisplay = screen.getDisplayNearestPoint( { x: this.windowBounds.x, y: this.windowBounds.y } )
|
||||
|
||||
const [left1, top1, right1, bottom1] = [this.windowBounds.x, this.windowBounds.y, this.windowBounds.x + this.windowBounds.width, this.windowBounds.y + this.windowBounds.height]
|
||||
const [left2, top2, right2, bottom2] = [closestDisplay.bounds.x, closestDisplay.bounds.y, closestDisplay.bounds.x + closestDisplay.bounds.width, closestDisplay.bounds.y + closestDisplay.bounds.height]
|
||||
|
||||
if ((left2 > right1 || right2 < left1 || top2 > bottom1 || bottom2 < top1) && !maximized) {
|
||||
bwOptions.x = closestDisplay.bounds.width / 2 - bwOptions.width / 2
|
||||
bwOptions.y = closestDisplay.bounds.height / 2 - bwOptions.height / 2
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.configStore.appearance || {}).frame === 'native') {
|
||||
bwOptions.frame = true
|
||||
} else {
|
||||
if (process.platform === 'darwin') {
|
||||
bwOptions.titleBarStyle = 'hiddenInset'
|
||||
}
|
||||
}
|
||||
|
||||
this.window = new BrowserWindow(bwOptions)
|
||||
|
||||
this.window.once('ready-to-show', () => {
|
||||
if (process.platform === 'darwin') {
|
||||
this.window.setVibrancy('window')
|
||||
} else if (process.platform === 'win32' && (this.configStore.appearance || {}).vibrancy) {
|
||||
this.setVibrancy(true)
|
||||
}
|
||||
|
||||
if (!options.hidden) {
|
||||
if (maximized) {
|
||||
this.window.maximize()
|
||||
} else {
|
||||
this.window.show()
|
||||
}
|
||||
this.window.focus()
|
||||
this.window.moveTop()
|
||||
}
|
||||
})
|
||||
|
||||
this.window.on('blur', () => {
|
||||
if (this.configStore.appearance?.dockHideOnBlur) {
|
||||
this.hide()
|
||||
}
|
||||
})
|
||||
|
||||
this.window.loadURL(`file://${app.getAppPath()}/dist/index.html?${this.window.id}`, { extraHeaders: 'pragma: no-cache\n' })
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
this.window.setMenu(null)
|
||||
}
|
||||
|
||||
this.setupWindowManagement()
|
||||
|
||||
this.ready = new Promise(resolve => {
|
||||
const listener = event => {
|
||||
if (event.sender === this.window.webContents) {
|
||||
ipcMain.removeListener('app:ready', listener as any)
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
ipcMain.on('app:ready', listener)
|
||||
})
|
||||
}
|
||||
|
||||
setVibrancy (enabled: boolean, type?: string): void {
|
||||
this.lastVibrancy = { enabled, type }
|
||||
if (process.platform === 'win32') {
|
||||
if (parseFloat(os.release()) >= 10) {
|
||||
glasstron.update(this.window, {
|
||||
windows: { blurType: enabled ? type === 'fluent' ? 'acrylic' : 'blurbehind' : null },
|
||||
})
|
||||
} else {
|
||||
DwmEnableBlurBehindWindow(this.window, enabled)
|
||||
}
|
||||
} else if (process.platform === 'linux') {
|
||||
glasstron.update(this.window, {
|
||||
linux: { requestBlur: enabled },
|
||||
})
|
||||
this.window.setBackgroundColor(enabled ? '#00000000' : '#131d27')
|
||||
} else {
|
||||
this.window.setVibrancy(enabled ? 'dark' : null as any) // electron issue 20269
|
||||
}
|
||||
}
|
||||
|
||||
show (): void {
|
||||
this.window.show()
|
||||
this.window.moveTop()
|
||||
}
|
||||
|
||||
focus (): void {
|
||||
this.window.focus()
|
||||
}
|
||||
|
||||
send (event: string, ...args: any[]): void {
|
||||
if (!this.window) {
|
||||
return
|
||||
}
|
||||
this.window.webContents.send(event, ...args)
|
||||
if (event === 'host:config-change') {
|
||||
this.configStore = args[0]
|
||||
}
|
||||
}
|
||||
|
||||
isDestroyed (): boolean {
|
||||
return !this.window || this.window.isDestroyed()
|
||||
}
|
||||
|
||||
isFocused (): boolean {
|
||||
return this.window.isFocused()
|
||||
}
|
||||
|
||||
hide (): void {
|
||||
if (process.platform === 'darwin') {
|
||||
// Lose focus
|
||||
Menu.sendActionToFirstResponder('hide:')
|
||||
}
|
||||
this.window.blur()
|
||||
if (process.platform !== 'darwin') {
|
||||
this.window.hide()
|
||||
}
|
||||
}
|
||||
|
||||
present (): void {
|
||||
if (!this.window.isVisible()) {
|
||||
// unfocused, invisible
|
||||
this.window.show()
|
||||
this.window.focus()
|
||||
} else {
|
||||
if (!this.configStore.appearance?.dock || this.configStore.appearance?.dock === 'off') {
|
||||
// not docked, visible
|
||||
setTimeout(() => {
|
||||
this.window.show()
|
||||
this.window.focus()
|
||||
})
|
||||
} else {
|
||||
if (this.configStore.appearance?.dockAlwaysOnTop) {
|
||||
// docked, visible, on top
|
||||
this.window.hide()
|
||||
} else {
|
||||
// docked, visible, not on top
|
||||
this.window.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleSecondInstance (argv: string[], cwd: string): void {
|
||||
this.send('host:second-instance', parseArgs(argv, cwd), cwd)
|
||||
}
|
||||
|
||||
private setupWindowManagement () {
|
||||
this.window.on('show', () => {
|
||||
this.visible.next(true)
|
||||
this.send('host:window-shown')
|
||||
})
|
||||
|
||||
this.window.on('hide', () => {
|
||||
this.visible.next(false)
|
||||
})
|
||||
|
||||
const moveSubscription = new Observable<void>(observer => {
|
||||
this.window.on('move', () => observer.next())
|
||||
}).pipe(debounceTime(250)).subscribe(() => {
|
||||
this.send('host:window-moved')
|
||||
})
|
||||
|
||||
this.window.on('closed', () => {
|
||||
moveSubscription.unsubscribe()
|
||||
})
|
||||
|
||||
this.window.on('enter-full-screen', () => this.send('host:window-enter-full-screen'))
|
||||
this.window.on('leave-full-screen', () => this.send('host:window-leave-full-screen'))
|
||||
|
||||
this.window.on('close', event => {
|
||||
if (!this.closing) {
|
||||
event.preventDefault()
|
||||
this.send('host:window-close-request')
|
||||
return
|
||||
}
|
||||
this.windowConfig.set('windowBoundaries', this.windowBounds)
|
||||
this.windowConfig.set('maximized', this.window.isMaximized())
|
||||
})
|
||||
|
||||
this.window.on('closed', () => {
|
||||
this.destroy()
|
||||
})
|
||||
|
||||
this.window.on('resize', () => {
|
||||
if (!this.window.isMaximized()) {
|
||||
this.windowBounds = this.window.getBounds()
|
||||
}
|
||||
})
|
||||
|
||||
this.window.on('move', () => {
|
||||
if (!this.window.isMaximized()) {
|
||||
this.windowBounds = this.window.getBounds()
|
||||
}
|
||||
})
|
||||
|
||||
this.window.on('focus', () => {
|
||||
this.send('host:window-focused')
|
||||
})
|
||||
|
||||
ipcMain.on('window-focus', event => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
this.window.focus()
|
||||
})
|
||||
|
||||
ipcMain.on('window-maximize', event => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
this.window.maximize()
|
||||
})
|
||||
|
||||
ipcMain.on('window-unmaximize', event => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
this.window.unmaximize()
|
||||
})
|
||||
|
||||
ipcMain.on('window-toggle-maximize', event => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
if (this.window.isMaximized()) {
|
||||
this.window.unmaximize()
|
||||
} else {
|
||||
this.window.maximize()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('window-minimize', event => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
this.window.minimize()
|
||||
})
|
||||
|
||||
ipcMain.on('window-set-bounds', (event, bounds) => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
this.window.setBounds(bounds)
|
||||
})
|
||||
|
||||
ipcMain.on('window-set-always-on-top', (event, flag) => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
this.window.setAlwaysOnTop(flag)
|
||||
})
|
||||
|
||||
ipcMain.on('window-set-vibrancy', (event, enabled, type) => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
this.setVibrancy(enabled, type)
|
||||
})
|
||||
|
||||
ipcMain.on('window-set-title', (event, title) => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
this.window.setTitle(title)
|
||||
})
|
||||
|
||||
ipcMain.on('window-bring-to-front', event => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
if (this.window.isMinimized()) {
|
||||
this.window.restore()
|
||||
}
|
||||
this.window.show()
|
||||
this.window.moveTop()
|
||||
})
|
||||
|
||||
ipcMain.on('window-close', event => {
|
||||
if (!this.window || event.sender !== this.window.webContents) {
|
||||
return
|
||||
}
|
||||
this.closing = true
|
||||
this.window.close()
|
||||
})
|
||||
|
||||
this.window.webContents.on('new-window', event => event.preventDefault())
|
||||
|
||||
ipcMain.on('window-set-disable-vibrancy-while-dragging', (_event, value) => {
|
||||
this.disableVibrancyWhileDragging = value
|
||||
})
|
||||
|
||||
this.window.on('will-move', () => {
|
||||
if (!this.lastVibrancy?.enabled || !this.disableVibrancyWhileDragging) {
|
||||
return
|
||||
}
|
||||
let timeout: number|null = null
|
||||
const oldVibrancy = this.lastVibrancy
|
||||
this.setVibrancy(false)
|
||||
const onMove = () => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
this.window.off('move', onMove)
|
||||
this.setVibrancy(oldVibrancy.enabled, oldVibrancy.type)
|
||||
}, 500)
|
||||
}
|
||||
this.window.on('move', onMove)
|
||||
})
|
||||
}
|
||||
|
||||
private destroy () {
|
||||
this.window = null
|
||||
this.closed.next()
|
||||
this.visible.complete()
|
||||
this.closed.complete()
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
{
|
||||
"name": "terminus",
|
||||
"description": "A terminal for a modern age",
|
||||
"repository": "https://github.com/eugeny/terminus",
|
||||
"author": {
|
||||
"name": "Eugene Pankov",
|
||||
"email": "e@ajenti.org"
|
||||
},
|
||||
"main": "dist/main.js",
|
||||
"version": "1.0.123-nightly.0",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^9.1.9",
|
||||
"@angular/common": "^9.1.11",
|
||||
"@angular/compiler": "^9.1.9",
|
||||
"@angular/core": "^9.1.9",
|
||||
"@angular/forms": "^9.1.11",
|
||||
"@angular/platform-browser": "^9.1.9",
|
||||
"@angular/platform-browser-dynamic": "^9.1.9",
|
||||
"@ng-bootstrap/ng-bootstrap": "^6.1.0",
|
||||
"@terminus-term/node-pty": "0.10.0-beta10",
|
||||
"devtron": "1.4.0",
|
||||
"electron-config": "2.0.0",
|
||||
"electron-debug": "^3.0.1",
|
||||
"electron-is-dev": "1.1.0",
|
||||
"fontmanager-redux": "1.0.0",
|
||||
"glasstron": "AryToNeX/Glasstron#dependabot/npm_and_yarn/electron-11.1.0",
|
||||
"js-yaml": "3.14.0",
|
||||
"keytar": "^7.2.0",
|
||||
"mz": "^2.7.0",
|
||||
"ngx-toastr": "^12.0.1",
|
||||
"npm": "7.0.15",
|
||||
"path": "0.12.7",
|
||||
"rxjs": "^6.5.5",
|
||||
"rxjs-compat": "^6.6.0",
|
||||
"yargs": "^15.4.1",
|
||||
"zone.js": "^0.11.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"macos-native-processlist": "^2.0.0",
|
||||
"serialport": "^9.0.4",
|
||||
"windows-blurbehind": "^1.0.1",
|
||||
"windows-native-registry": "^3.0.0",
|
||||
"windows-process-tree": "^0.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"terminus-core": "*",
|
||||
"terminus-settings": "*",
|
||||
"terminus-serial": "*",
|
||||
"terminus-plugin-manager": "*",
|
||||
"terminus-community-color-schemes": "*",
|
||||
"terminus-ssh": "*",
|
||||
"terminus-terminal": "*"
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { NgModule } from '@angular/core'
|
||||
import { BrowserModule } from '@angular/platform-browser'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ToastrModule } from 'ngx-toastr'
|
||||
|
||||
export function getRootModule (plugins: any[]) {
|
||||
const imports = [
|
||||
BrowserModule,
|
||||
...plugins,
|
||||
NgbModule,
|
||||
ToastrModule.forRoot({
|
||||
positionClass: 'toast-bottom-center',
|
||||
toastClass: 'toast',
|
||||
preventDuplicates: true,
|
||||
extendedTimeOut: 5000,
|
||||
}),
|
||||
]
|
||||
const bootstrap = [
|
||||
...plugins.filter(x => x.bootstrap).map(x => x.bootstrap),
|
||||
]
|
||||
|
||||
if (bootstrap.length === 0) {
|
||||
throw new Error('Did not find any bootstrap components. Are there any plugins installed?')
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports,
|
||||
bootstrap,
|
||||
}) class RootModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
|
||||
|
||||
return RootModule
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import '../lib/lru'
|
||||
import 'source-sans-pro/source-sans-pro.css'
|
||||
import 'source-code-pro/source-code-pro.css'
|
||||
import '@fortawesome/fontawesome-free/css/solid.css'
|
||||
import '@fortawesome/fontawesome-free/css/brands.css'
|
||||
import '@fortawesome/fontawesome-free/css/regular.css'
|
||||
import '@fortawesome/fontawesome-free/css/fontawesome.css'
|
||||
import 'ngx-toastr/toastr.css'
|
||||
import './preload.scss'
|
@ -1,65 +0,0 @@
|
||||
import 'zone.js'
|
||||
import 'core-js/proposals/reflect-metadata'
|
||||
import 'rxjs'
|
||||
|
||||
import * as isDev from 'electron-is-dev'
|
||||
|
||||
import './global.scss'
|
||||
import './toastr.scss'
|
||||
|
||||
import { enableProdMode, NgModuleRef, ApplicationRef } from '@angular/core'
|
||||
import { enableDebugTools } from '@angular/platform-browser'
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
||||
|
||||
import { getRootModule } from './app.module'
|
||||
import { findPlugins, loadPlugins, PluginInfo } from './plugins'
|
||||
|
||||
// Always land on the start view
|
||||
location.hash = ''
|
||||
|
||||
;(process as any).enablePromiseAPI = true
|
||||
|
||||
if (process.platform === 'win32' && !('HOME' in process.env)) {
|
||||
process.env.HOME = `${process.env.HOMEDRIVE}${process.env.HOMEPATH}`
|
||||
}
|
||||
|
||||
if (isDev) {
|
||||
console.warn('Running in debug mode')
|
||||
} else {
|
||||
enableProdMode()
|
||||
}
|
||||
|
||||
async function bootstrap (plugins: PluginInfo[], safeMode = false): Promise<NgModuleRef<any>> {
|
||||
if (safeMode) {
|
||||
plugins = plugins.filter(x => x.isBuiltin)
|
||||
}
|
||||
const pluginsModules = await loadPlugins(plugins, (current, total) => {
|
||||
(document.querySelector('.progress .bar') as HTMLElement).style.width = `${100 * current / total}%` // eslint-disable-line
|
||||
})
|
||||
const module = getRootModule(pluginsModules)
|
||||
window['rootModule'] = module
|
||||
return platformBrowserDynamic().bootstrapModule(module).then(moduleRef => {
|
||||
if (isDev) {
|
||||
const applicationRef = moduleRef.injector.get(ApplicationRef)
|
||||
const componentRef = applicationRef.components[0]
|
||||
enableDebugTools(componentRef)
|
||||
}
|
||||
return moduleRef
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
@ -1,97 +0,0 @@
|
||||
body {
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
background: #1D272D;
|
||||
}
|
||||
|
||||
.modal-dialog, .modal-backdrop, .no-drag {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.selectable {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
[ngbradiogroup] input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn {
|
||||
& > svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.form-line {
|
||||
display: flex;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.2);
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
margin: 0;
|
||||
min-height: 64px;
|
||||
|
||||
.header {
|
||||
margin-right: auto;
|
||||
|
||||
.title {
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 13px;
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
|
||||
&>.form-control, &>.input-group {
|
||||
width: 33%;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
|
||||
&:focus {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
@mixin thumb() {
|
||||
-webkit-appearance: none;
|
||||
display: block;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
background: #aaa;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
margin-top: -4px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.95);
|
||||
transition: 0.25s background;
|
||||
|
||||
&:hover {
|
||||
background: #777;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb { @include thumb(); }
|
||||
&::-moz-range-thumb { @include thumb(); }
|
||||
&::-ms-thumb { @include thumb(); }
|
||||
&::thumb { @include thumb(); }
|
||||
|
||||
@mixin track() {
|
||||
height: 4px;
|
||||
background: #111;
|
||||
margin: 3px 0 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&::-webkit-slider-runnable-track { @include track(); }
|
||||
&:focus::-webkit-slider-runnable-track { @include track(); }
|
||||
&::-moz-range-track { @include track(); }
|
||||
&::-ms-track { @include track(); }
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
import * as fs from 'mz/fs'
|
||||
import * as path from 'path'
|
||||
const nodeModule = require('module') // eslint-disable-line @typescript-eslint/no-var-requires
|
||||
const nodeRequire = (global as any).require
|
||||
|
||||
function normalizePath (path: string): string {
|
||||
const cygwinPrefix = '/cygdrive/'
|
||||
if (path.startsWith(cygwinPrefix)) {
|
||||
path = path.substring(cygwinPrefix.length).replace('/', '\\')
|
||||
path = path[0] + ':' + path.substring(1)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
global['module'].paths.map((x: string) => nodeModule.globalPaths.push(normalizePath(x)))
|
||||
|
||||
if (process.env.TERMINUS_DEV) {
|
||||
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
|
||||
}
|
||||
|
||||
const builtinPluginsPath = process.env.TERMINUS_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('userData'),
|
||||
'plugins',
|
||||
)
|
||||
|
||||
if (!fs.existsSync(userPluginsPath)) {
|
||||
fs.mkdir(userPluginsPath)
|
||||
}
|
||||
|
||||
Object.assign(window, { builtinPluginsPath, userPluginsPath })
|
||||
nodeModule.globalPaths.unshift(builtinPluginsPath)
|
||||
nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules'))
|
||||
// nodeModule.globalPaths.unshift(path.join((process as any).resourcesPath, 'app.asar', 'node_modules'))
|
||||
if (process.env.TERMINUS_PLUGINS) {
|
||||
process.env.TERMINUS_PLUGINS.split(':').map(x => nodeModule.globalPaths.push(normalizePath(x)))
|
||||
}
|
||||
|
||||
export type ProgressCallback = (current: number, total: number) => void // eslint-disable-line @typescript-eslint/no-type-alias
|
||||
|
||||
export interface PluginInfo {
|
||||
name: string
|
||||
description: string
|
||||
packageName: string
|
||||
isBuiltin: boolean
|
||||
version: string
|
||||
author: string
|
||||
homepage?: string
|
||||
path?: string
|
||||
info?: any
|
||||
}
|
||||
|
||||
const builtinModules = [
|
||||
'@angular/animations',
|
||||
'@angular/common',
|
||||
'@angular/compiler',
|
||||
'@angular/core',
|
||||
'@angular/forms',
|
||||
'@angular/platform-browser',
|
||||
'@angular/platform-browser-dynamic',
|
||||
'@ng-bootstrap/ng-bootstrap',
|
||||
'ngx-toastr',
|
||||
'rxjs',
|
||||
'rxjs/operators',
|
||||
'rxjs-compat/Subject',
|
||||
'terminus-core',
|
||||
'terminus-settings',
|
||||
'terminus-terminal',
|
||||
'zone.js/dist/zone.js',
|
||||
]
|
||||
|
||||
const cachedBuiltinModules = {}
|
||||
builtinModules.forEach(m => {
|
||||
const label = 'Caching ' + m
|
||||
console.time(label)
|
||||
cachedBuiltinModules[m] = nodeRequire(m)
|
||||
console.timeEnd(label)
|
||||
})
|
||||
|
||||
const originalRequire = (global as any).require
|
||||
;(global as any).require = function (query: string) {
|
||||
if (cachedBuiltinModules[query]) {
|
||||
return cachedBuiltinModules[query]
|
||||
}
|
||||
return originalRequire.apply(this, [query])
|
||||
}
|
||||
|
||||
const originalModuleRequire = nodeModule.prototype.require
|
||||
nodeModule.prototype.require = function (query: string) {
|
||||
if (cachedBuiltinModules[query]) {
|
||||
return cachedBuiltinModules[query]
|
||||
}
|
||||
return originalModuleRequire.call(this, query)
|
||||
}
|
||||
|
||||
export async function findPlugins (): Promise<PluginInfo[]> {
|
||||
const paths = nodeModule.globalPaths
|
||||
let foundPlugins: PluginInfo[] = []
|
||||
const candidateLocations: { pluginDir: string, packageName: string }[] = []
|
||||
const PREFIX = 'terminus-'
|
||||
|
||||
for (let pluginDir of paths) {
|
||||
pluginDir = normalizePath(pluginDir)
|
||||
if (!await fs.exists(pluginDir)) {
|
||||
continue
|
||||
}
|
||||
const pluginNames = await fs.readdir(pluginDir)
|
||||
if (await fs.exists(path.join(pluginDir, 'package.json'))) {
|
||||
candidateLocations.push({
|
||||
pluginDir: path.dirname(pluginDir),
|
||||
packageName: path.basename(pluginDir),
|
||||
})
|
||||
}
|
||||
for (const packageName of pluginNames) {
|
||||
if (packageName.startsWith(PREFIX)) {
|
||||
candidateLocations.push({ pluginDir, packageName })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const { pluginDir, packageName } of candidateLocations) {
|
||||
const pluginPath = path.join(pluginDir, packageName)
|
||||
const infoPath = path.join(pluginPath, 'package.json')
|
||||
if (!await fs.exists(infoPath)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const name = packageName.substring(PREFIX.length)
|
||||
|
||||
if (foundPlugins.some(x => x.name === name)) {
|
||||
console.info(`Plugin ${packageName} already exists, overriding`)
|
||||
foundPlugins = foundPlugins.filter(x => x.name !== name)
|
||||
}
|
||||
|
||||
try {
|
||||
const info = JSON.parse(await fs.readFile(infoPath, { encoding: 'utf-8' }))
|
||||
if (!info.keywords || !(info.keywords.includes('terminus-plugin') || info.keywords.includes('terminus-builtin-plugin'))) {
|
||||
continue
|
||||
}
|
||||
let author = info.author
|
||||
author = author.name || author
|
||||
foundPlugins.push({
|
||||
name: name,
|
||||
packageName: packageName,
|
||||
isBuiltin: pluginDir === builtinPluginsPath,
|
||||
version: info.version,
|
||||
description: info.description,
|
||||
author,
|
||||
path: pluginPath,
|
||||
info,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Cannot load package info for', packageName)
|
||||
}
|
||||
}
|
||||
|
||||
foundPlugins.sort((a, b) => a.name > b.name ? 1 : -1)
|
||||
|
||||
;(window as any).installedPlugins = foundPlugins
|
||||
return foundPlugins
|
||||
}
|
||||
|
||||
export async function loadPlugins (foundPlugins: PluginInfo[], progress: ProgressCallback): Promise<any[]> {
|
||||
const plugins: any[] = []
|
||||
progress(0, 1)
|
||||
let index = 0
|
||||
for (const foundPlugin of foundPlugins) {
|
||||
console.info(`Loading ${foundPlugin.name}: ${nodeRequire.resolve(foundPlugin.path)}`)
|
||||
progress(index, foundPlugins.length)
|
||||
try {
|
||||
const label = 'Loading ' + foundPlugin.name
|
||||
console.time(label)
|
||||
const packageModule = nodeRequire(foundPlugin.path)
|
||||
const pluginModule = packageModule.default.forRoot ? packageModule.default.forRoot() : packageModule.default
|
||||
pluginModule['pluginName'] = foundPlugin.name
|
||||
pluginModule['bootstrap'] = packageModule.bootstrap
|
||||
plugins.push(pluginModule)
|
||||
console.timeEnd(label)
|
||||
await new Promise(x => setTimeout(x, 50))
|
||||
} catch (error) {
|
||||
console.error(`Could not load ${foundPlugin.name}:`, error)
|
||||
}
|
||||
index++
|
||||
}
|
||||
progress(1, 1)
|
||||
return plugins
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
.preload-logo {
|
||||
-webkit-app-region: drag;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
animation: 0.5s ease-out fadeIn;
|
||||
|
||||
&>div {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
margin: auto;
|
||||
flex: none;
|
||||
|
||||
.progress {
|
||||
background: rgba(0,0,0,.25);
|
||||
height: 3px;
|
||||
margin: 10px 50px;
|
||||
|
||||
.bar {
|
||||
transition: 1s ease-out width;
|
||||
background: #a1c5e4;
|
||||
height: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
.terminus-logo {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
background: url('../assets/logo.svg');
|
||||
background-repeat: none;
|
||||
background-size: contain;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
|
||||
.terminus-title {
|
||||
color: #a1c5e4;
|
||||
font-family: 'Source Sans Pro';
|
||||
text-align: center;
|
||||
font-weight: normal;
|
||||
font-size: 42px;
|
||||
margin: 0;
|
||||
|
||||
sup {
|
||||
color: #842fe0;
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
import { Component } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
template: '<app-root></app-root>',
|
||||
})
|
||||
export class RootComponent { } // eslint-disable-line @typescript-eslint/no-extraneous-class
|
@ -1,21 +0,0 @@
|
||||
#toast-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
|
||||
.toast {
|
||||
box-shadow: 0 1px 0 rgba(0,0,0,.25);
|
||||
padding: 10px;
|
||||
background-image: none;
|
||||
width: auto;
|
||||
|
||||
&.toast-error {
|
||||
background-color: #BD362F;
|
||||
}
|
||||
|
||||
&.toast-info {
|
||||
background-color: #555;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./src",
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"sourceMap": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUnusedParameters": true,
|
||||
"noUnusedLocals": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015",
|
||||
"es2015.iterable",
|
||||
"es2017",
|
||||
"es7"
|
||||
]
|
||||
},
|
||||
"compileOnSave": false,
|
||||
"exclude": [
|
||||
"dist",
|
||||
"node_modules",
|
||||
"*/node_modules",
|
||||
"terminus*",
|
||||
"platforms"
|
||||
]
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./lib",
|
||||
"module": "commonjs",
|
||||
"target": "es2017",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"sourceMap": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUnusedLocals": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015",
|
||||
"es2015.iterable",
|
||||
"es2017",
|
||||
"es7"
|
||||
]
|
||||
},
|
||||
"compileOnSave": false,
|
||||
"exclude": [
|
||||
"dist",
|
||||
"node_modules",
|
||||
"*/node_modules"
|
||||
]
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
|
||||
module.exports = {
|
||||
name: 'terminus',
|
||||
target: 'node',
|
||||
entry: {
|
||||
'index.ignore': 'file-loader?name=index.html!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
|
||||
sentry: path.resolve(__dirname, 'lib/sentry.ts'),
|
||||
preload: path.resolve(__dirname, 'src/entry.preload.ts'),
|
||||
bundle: path.resolve(__dirname, 'src/entry.ts'),
|
||||
},
|
||||
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
|
||||
optimization:{
|
||||
minimize: false,
|
||||
},
|
||||
context: __dirname,
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
pathinfo: true,
|
||||
filename: '[name].js',
|
||||
},
|
||||
resolve: {
|
||||
modules: ['src/', 'node_modules', '../node_modules', 'assets/'].map(x => path.join(__dirname, x)),
|
||||
extensions: ['.ts', '.js'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: {
|
||||
loader: 'awesome-typescript-loader',
|
||||
options: {
|
||||
configFileName: path.resolve(__dirname, 'tsconfig.json'),
|
||||
},
|
||||
},
|
||||
},
|
||||
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
|
||||
{ test: /\.css$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
|
||||
{
|
||||
test: /\.(png|svg)$/,
|
||||
use: {
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: 'images/[name].[ext]',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
use: {
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: 'fonts/[name].[ext]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
externals: {
|
||||
'@angular/core': 'commonjs @angular/core',
|
||||
'@angular/compiler': 'commonjs @angular/compiler',
|
||||
'@angular/platform-browser': 'commonjs @angular/platform-browser',
|
||||
'@angular/platform-browser-dynamic': 'commonjs @angular/platform-browser-dynamic',
|
||||
'@angular/forms': 'commonjs @angular/forms',
|
||||
'@angular/common': 'commonjs @angular/common',
|
||||
'@ng-bootstrap/ng-bootstrap': 'commonjs @ng-bootstrap/ng-bootstrap',
|
||||
child_process: 'commonjs child_process',
|
||||
electron: 'commonjs electron',
|
||||
'electron-is-dev': 'commonjs electron-is-dev',
|
||||
fs: 'commonjs fs',
|
||||
'ngx-toastr': 'commonjs ngx-toastr',
|
||||
module: 'commonjs module',
|
||||
mz: 'commonjs mz',
|
||||
path: 'commonjs path',
|
||||
rxjs: 'commonjs rxjs',
|
||||
'zone.js': 'commonjs zone.js/dist/zone.js',
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.ModuleConcatenationPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.type': '"renderer"'
|
||||
}),
|
||||
],
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
|
||||
module.exports = {
|
||||
name: 'terminus-main',
|
||||
target: 'node',
|
||||
entry: {
|
||||
main: path.resolve(__dirname, 'lib/index.ts'),
|
||||
},
|
||||
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
|
||||
context: __dirname,
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
pathinfo: true,
|
||||
filename: '[name].js',
|
||||
},
|
||||
resolve: {
|
||||
modules: ['lib/', 'node_modules', '../node_modules'].map(x => path.join(__dirname, x)),
|
||||
extensions: ['.ts', '.js'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: {
|
||||
loader: 'awesome-typescript-loader',
|
||||
options: {
|
||||
configFileName: path.resolve(__dirname, 'tsconfig.main.json'),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
externals: {
|
||||
electron: 'commonjs electron',
|
||||
'electron-config': 'commonjs electron-config',
|
||||
'electron-vibrancy': 'commonjs electron-vibrancy',
|
||||
fs: 'commonjs fs',
|
||||
glasstron: 'commonjs glasstron',
|
||||
mz: 'commonjs mz',
|
||||
path: 'commonjs path',
|
||||
yargs: 'commonjs yargs',
|
||||
'windows-swca': 'commonjs windows-swca',
|
||||
'windows-blurbehind': 'commonjs windows-blurbehind',
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.ModuleConcatenationPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.type': '"main"',
|
||||
}),
|
||||
],
|
||||
}
|