mirror of
https://github.com/Eugeny/tabby.git
synced 2025-09-17 13:54:34 +00:00
Compare commits
15 Commits
v1.0.0-alp
...
v1.0.0-alp
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f84fd07857 | ||
![]() |
24c59b88ca | ||
![]() |
e45090cc89 | ||
![]() |
f53b96eba8 | ||
![]() |
80699ee13f | ||
![]() |
7e7d537868 | ||
![]() |
1afb1e718b | ||
![]() |
f71f518058 | ||
![]() |
7a005132cc | ||
![]() |
34ef809aee | ||
![]() |
6352f22c48 | ||
![]() |
d0f378764f | ||
![]() |
7885badbfd | ||
![]() |
5999d169bc | ||
![]() |
40b0f8cb69 |
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'
|
|||||||
import { BrowserModule } from '@angular/platform-browser'
|
import { BrowserModule } from '@angular/platform-browser'
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
export async function getRootModule (plugins: any[]): Promise<any> {
|
export function getRootModule (plugins: any[]) {
|
||||||
let imports = [
|
let imports = [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
...(plugins.map(x => x.default.forRoot ? x.default.forRoot() : x.default)),
|
...(plugins.map(x => x.default.forRoot ? x.default.forRoot() : x.default)),
|
||||||
|
@@ -6,11 +6,11 @@ import 'rxjs'
|
|||||||
// Always land on the start view
|
// Always land on the start view
|
||||||
location.hash = ''
|
location.hash = ''
|
||||||
|
|
||||||
import { enableProdMode } from '@angular/core'
|
import { enableProdMode, NgModuleRef } from '@angular/core'
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
||||||
|
|
||||||
import { getRootModule } from './app.module'
|
import { getRootModule } from './app.module'
|
||||||
import { findPlugins, loadPlugins } from './plugins'
|
import { findPlugins, loadPlugins, IPluginInfo } from './plugins'
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH
|
process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH
|
||||||
@@ -22,10 +22,29 @@ if (require('electron-is-dev')) {
|
|||||||
enableProdMode()
|
enableProdMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
findPlugins().then(async plugins => {
|
async function bootstrap (plugins: IPluginInfo[], safeMode = false): Promise<NgModuleRef<any>> {
|
||||||
|
if (safeMode) {
|
||||||
|
plugins = plugins.filter(x => x.isBuiltin)
|
||||||
|
}
|
||||||
let pluginsModules = await loadPlugins(plugins, (current, total) => {
|
let pluginsModules = await loadPlugins(plugins, (current, total) => {
|
||||||
(document.querySelector('.progress .bar') as HTMLElement).style.width = 100 * current / total + '%'
|
(document.querySelector('.progress .bar') as HTMLElement).style.width = 100 * current / total + '%'
|
||||||
})
|
})
|
||||||
let module = await getRootModule(pluginsModules)
|
let module = getRootModule(pluginsModules)
|
||||||
platformBrowserDynamic().bootstrapModule(module)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@@ -20,7 +20,7 @@ if (process.env.DEV) {
|
|||||||
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
|
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(
|
const userPluginsPath = path.join(
|
||||||
require('electron').remote.app.getPath('appData'),
|
require('electron').remote.app.getPath('appData'),
|
||||||
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 361 KiB |
@@ -74,7 +74,8 @@
|
|||||||
"libnotify4",
|
"libnotify4",
|
||||||
"libappindicator1",
|
"libappindicator1",
|
||||||
"libxtst6",
|
"libxtst6",
|
||||||
"libnss3"
|
"libnss3",
|
||||||
|
"tmux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"rpm": {
|
"rpm": {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { Component, Inject, Input, HostListener } from '@angular/core'
|
import { Component, Inject, Input, HostListener } from '@angular/core'
|
||||||
import { trigger, style, animate, transition, state } from '@angular/animations'
|
import { trigger, style, animate, transition, state } from '@angular/animations'
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
import { ElectronService } from '../services/electron.service'
|
import { ElectronService } from '../services/electron.service'
|
||||||
import { HostAppService, Platform } from '../services/hostApp.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 { TabRecoveryService } from '../services/tabRecovery.service'
|
||||||
import { ThemesService } from '../services/themes.service'
|
import { ThemesService } from '../services/themes.service'
|
||||||
|
|
||||||
|
import { SafeModeModalComponent } from './safeModeModal.component'
|
||||||
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -62,6 +64,7 @@ export class AppRootComponent {
|
|||||||
public app: AppService,
|
public app: AppService,
|
||||||
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
||||||
log: LogService,
|
log: LogService,
|
||||||
|
ngbModal: NgbModal,
|
||||||
_themes: ThemesService,
|
_themes: ThemesService,
|
||||||
) {
|
) {
|
||||||
this.logger = log.create('main')
|
this.logger = log.create('main')
|
||||||
@@ -104,6 +107,10 @@ export class AppRootComponent {
|
|||||||
this.hotkeys.globalHotkey.subscribe(() => {
|
this.hotkeys.globalHotkey.subscribe(() => {
|
||||||
this.onGlobalHotkey()
|
this.onGlobalHotkey()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (window['safeModeReason']) {
|
||||||
|
ngbModal.open(SafeModeModalComponent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onGlobalHotkey () {
|
onGlobalHotkey () {
|
||||||
|
7
terminus-core/src/components/safeModeModal.component.pug
Normal file
7
terminus-core/src/components/safeModeModal.component.pug
Normal file
@@ -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
|
19
terminus-core/src/components/safeModeModal.component.ts
Normal file
19
terminus-core/src/components/safeModeModal.component.ts
Normal file
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
@@ -17,6 +17,7 @@ import { ThemesService } from './services/themes.service'
|
|||||||
|
|
||||||
import { AppRootComponent } from './components/appRoot.component'
|
import { AppRootComponent } from './components/appRoot.component'
|
||||||
import { TabBodyComponent } from './components/tabBody.component'
|
import { TabBodyComponent } from './components/tabBody.component'
|
||||||
|
import { SafeModeModalComponent } from './components/safeModeModal.component'
|
||||||
import { StartPageComponent } from './components/startPage.component'
|
import { StartPageComponent } from './components/startPage.component'
|
||||||
import { TabHeaderComponent } from './components/tabHeader.component'
|
import { TabHeaderComponent } from './components/tabHeader.component'
|
||||||
import { TitleBarComponent } from './components/titleBar.component'
|
import { TitleBarComponent } from './components/titleBar.component'
|
||||||
@@ -65,9 +66,11 @@ const PROVIDERS = [
|
|||||||
TitleBarComponent,
|
TitleBarComponent,
|
||||||
WindowControlsComponent,
|
WindowControlsComponent,
|
||||||
RenameTabModalComponent,
|
RenameTabModalComponent,
|
||||||
|
SafeModeModalComponent,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
RenameTabModalComponent,
|
RenameTabModalComponent,
|
||||||
|
SafeModeModalComponent,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export default class AppModule {
|
export default class AppModule {
|
||||||
|
@@ -38,7 +38,7 @@ export class ConfigProxy {
|
|||||||
{
|
{
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: false,
|
configurable: false,
|
||||||
get: () => real[key] || defaults[key],
|
get: () => (real[key] !== undefined) ? real[key] : defaults[key],
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
real[key] = value
|
real[key] = value
|
||||||
}
|
}
|
||||||
|
@@ -76,6 +76,13 @@ $list-group-border-color: rgba(255,255,255,.1);
|
|||||||
$list-group-hover-bg: rgba(255,255,255,.1);
|
$list-group-hover-bg: rgba(255,255,255,.1);
|
||||||
$list-group-link-active-bg: rgba(255,255,255,.2);
|
$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';
|
@import '~bootstrap/scss/bootstrap.scss';
|
||||||
|
|
||||||
|
@@ -182,7 +182,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
this.mouseEvent$.next(event)
|
this.mouseEvent$.next(event)
|
||||||
if (event.type === 'mousewheel') {
|
if (event.type === 'mousewheel') {
|
||||||
if (event.ctrlKey || event.metaKey) {
|
if (event.ctrlKey || event.metaKey) {
|
||||||
if (event.wheelDeltaY < 0) {
|
if (event.wheelDeltaY > 0) {
|
||||||
this.zoomIn()
|
this.zoomIn()
|
||||||
} else {
|
} else {
|
||||||
this.zoomOut()
|
this.zoomOut()
|
||||||
@@ -214,6 +214,13 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _measureCharacterSize = hterm.scrollPort_.measureCharacterSize.bind(hterm.scrollPort_)
|
||||||
|
hterm.scrollPort_.measureCharacterSize = () => {
|
||||||
|
let size = _measureCharacterSize()
|
||||||
|
size.height += this.config.store.terminal.linePadding
|
||||||
|
return size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attachIOHandlers (io: any) {
|
attachIOHandlers (io: any) {
|
||||||
@@ -261,6 +268,8 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
preferenceManager.set('ctrl-plus-minus-zero-zoom', false)
|
preferenceManager.set('ctrl-plus-minus-zero-zoom', false)
|
||||||
preferenceManager.set('scrollbar-visible', this.hostApp.platform === Platform.macOS)
|
preferenceManager.set('scrollbar-visible', this.hostApp.platform === Platform.macOS)
|
||||||
preferenceManager.set('copy-on-select', false)
|
preferenceManager.set('copy-on-select', false)
|
||||||
|
preferenceManager.set('alt-sends-what', 'browser-key')
|
||||||
|
preferenceManager.set('alt-gr-mode', 'ctrl-alt')
|
||||||
|
|
||||||
if (config.terminal.colorScheme.foreground) {
|
if (config.terminal.colorScheme.foreground) {
|
||||||
preferenceManager.set('foreground-color', config.terminal.colorScheme.foreground)
|
preferenceManager.set('foreground-color', config.terminal.colorScheme.foreground)
|
||||||
|
@@ -4,6 +4,7 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
defaults = {
|
defaults = {
|
||||||
terminal: {
|
terminal: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
linePadding: 0,
|
||||||
bell: 'off',
|
bell: 'off',
|
||||||
bracketedPaste: false,
|
bracketedPaste: false,
|
||||||
background: 'theme',
|
background: 'theme',
|
||||||
|
@@ -10,6 +10,11 @@ x-screen {
|
|||||||
transition: 0.125s ease background;
|
transition: 0.125s ease background;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x-row > span {
|
||||||
|
display: inline-block;
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "monospace-fallback";
|
font-family: "monospace-fallback";
|
||||||
src: url(fonts/Meslo.otf) format("opentype");
|
src: url(fonts/Meslo.otf) format("opentype");
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
import * as fs from 'mz/fs'
|
import * as fs from 'mz/fs'
|
||||||
|
import * as path from 'path'
|
||||||
import { exec, spawn } from 'mz/child_process'
|
import { exec, spawn } from 'mz/child_process'
|
||||||
import { exec as execAsync, execFileSync } from 'child_process'
|
import { exec as execAsync, execFileSync } from 'child_process'
|
||||||
|
|
||||||
import { AsyncSubject } from 'rxjs'
|
import { AsyncSubject } from 'rxjs'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Logger, LogService } from 'terminus-core'
|
import { Logger, LogService, ElectronService } from 'terminus-core'
|
||||||
import { SessionOptions, SessionPersistenceProvider } from '../api'
|
import { SessionOptions, SessionPersistenceProvider } from '../api'
|
||||||
|
|
||||||
declare function delay (ms: number): Promise<void>
|
declare function delay (ms: number): Promise<void>
|
||||||
@@ -35,6 +36,7 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
|
|||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
log: LogService,
|
log: LogService,
|
||||||
|
private electron: ElectronService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.logger = log.create('main')
|
this.logger = log.create('main')
|
||||||
@@ -115,7 +117,7 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async prepareConfig (): Promise<string> {
|
private async prepareConfig (): Promise<string> {
|
||||||
let configPath = '/tmp/.termScreenConfig'
|
let configPath = path.join(this.electron.app.getPath('userData'), 'screen-config.tmp')
|
||||||
await fs.writeFile(configPath, `
|
await fs.writeFile(configPath, `
|
||||||
escape ^^^
|
escape ^^^
|
||||||
vbell off
|
vbell off
|
||||||
|
@@ -15,6 +15,7 @@ const TMUX_CONFIG = `
|
|||||||
set -g set-titles-string "#W"
|
set -g set-titles-string "#W"
|
||||||
set -g window-status-format '#I:#(pwd="#{pane_current_path}"; echo \${pwd####*/})#F'
|
set -g window-status-format '#I:#(pwd="#{pane_current_path}"; echo \${pwd####*/})#F'
|
||||||
set -g window-status-current-format '#I:#(pwd="#{pane_current_path}"; echo \${pwd####*/})#F'
|
set -g window-status-current-format '#I:#(pwd="#{pane_current_path}"; echo \${pwd####*/})#F'
|
||||||
|
set-option -g prefix C-^
|
||||||
set-option -g status-interval 1
|
set-option -g status-interval 1
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@@ -221,6 +221,9 @@ export class SessionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getPersistence (): SessionPersistenceProvider {
|
private getPersistence (): SessionPersistenceProvider {
|
||||||
|
if (!this.config.store.terminal.persistence) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
return this.persistenceProviders.find(x => x.id === this.config.store.terminal.persistence) || null
|
return this.persistenceProviders.find(x => x.id === this.config.store.terminal.persistence) || null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,7 @@ export class Cygwin32ShellProvider extends ShellProvider {
|
|||||||
let cygwinPath = await new Promise<string>(resolve => {
|
let cygwinPath = await new Promise<string>(resolve => {
|
||||||
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup', arch: 'x86' })
|
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup', arch: 'x86' })
|
||||||
reg.get('rootdir', (err, item) => {
|
reg.get('rootdir', (err, item) => {
|
||||||
if (err) {
|
if (err || !item) {
|
||||||
return resolve(null)
|
return resolve(null)
|
||||||
}
|
}
|
||||||
resolve(item.value)
|
resolve(item.value)
|
||||||
|
@@ -25,7 +25,7 @@ export class Cygwin64ShellProvider extends ShellProvider {
|
|||||||
let cygwinPath = await new Promise<string>(resolve => {
|
let cygwinPath = await new Promise<string>(resolve => {
|
||||||
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup', arch: 'x64' })
|
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup', arch: 'x64' })
|
||||||
reg.get('rootdir', (err, item) => {
|
reg.get('rootdir', (err, item) => {
|
||||||
if (err) {
|
if (err || !item) {
|
||||||
return resolve(null)
|
return resolve(null)
|
||||||
}
|
}
|
||||||
resolve(item.value)
|
resolve(item.value)
|
||||||
|
@@ -25,7 +25,7 @@ export class GitBashShellProvider extends ShellProvider {
|
|||||||
let gitBashPath = await new Promise<string>(resolve => {
|
let gitBashPath = await new Promise<string>(resolve => {
|
||||||
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\GitForWindows' })
|
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\GitForWindows' })
|
||||||
reg.get('InstallPath', (err, item) => {
|
reg.get('InstallPath', (err, item) => {
|
||||||
if (err) {
|
if (err || !item) {
|
||||||
resolve(null)
|
resolve(null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,19 @@ export class GitBashShellProvider extends ShellProvider {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!gitBashPath) {
|
||||||
|
gitBashPath = await new Promise<string>(resolve => {
|
||||||
|
let reg = new Registry({ hive: Registry.HKCU, key: '\\Software\\GitForWindows' })
|
||||||
|
reg.get('InstallPath', (err, item) => {
|
||||||
|
if (err || !item) {
|
||||||
|
resolve(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve(item.value)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!gitBashPath) {
|
if (!gitBashPath) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -41,6 +54,7 @@ export class GitBashShellProvider extends ShellProvider {
|
|||||||
id: 'git-bash',
|
id: 'git-bash',
|
||||||
name: 'Git-Bash',
|
name: 'Git-Bash',
|
||||||
command: path.join(gitBashPath, 'bin', 'bash.exe'),
|
command: path.join(gitBashPath, 'bin', 'bash.exe'),
|
||||||
|
args: [ '--login', '-i' ],
|
||||||
env: {
|
env: {
|
||||||
TERM: 'cygwin',
|
TERM: 'cygwin',
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user