1
1
mirror of https://github.com/Eugeny/tabby.git synced 2024-12-25 19:42:42 +03:00

shell context menu integration (fixes #362)

This commit is contained in:
Eugene Pankov 2018-09-23 09:23:20 -07:00
parent 9ad1371c2b
commit 7f8d012a8a
7 changed files with 115 additions and 26 deletions

View File

@ -43,6 +43,7 @@
"dependencies": { "dependencies": {
"deepmerge": "^1.5.0", "deepmerge": "^1.5.0",
"js-yaml": "^3.9.0", "js-yaml": "^3.9.0",
"winreg": "^1.2.4",
"winston": "^2.4.0" "winston": "^2.4.0"
}, },
"false": {} "false": {}

View File

@ -13,4 +13,5 @@ export { Logger, LogService } from '../services/log.service'
export { HomeBaseService } from '../services/homeBase.service' export { HomeBaseService } from '../services/homeBase.service'
export { HotkeysService } from '../services/hotkeys.service' export { HotkeysService } from '../services/hotkeys.service'
export { HostAppService, Platform } from '../services/hostApp.service' export { HostAppService, Platform } from '../services/hostApp.service'
export { ShellIntegrationService } from '../services/shellIntegration.service'
export { ThemesService } from '../services/themes.service' export { ThemesService } from '../services/themes.service'

View File

@ -14,6 +14,7 @@ import { LogService } from './services/log.service'
import { HomeBaseService } from './services/homeBase.service' import { HomeBaseService } from './services/homeBase.service'
import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service' import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service'
import { DockingService } from './services/docking.service' import { DockingService } from './services/docking.service'
import { ShellIntegrationService } from './services/shellIntegration.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 { TouchbarService } from './services/touchbar.service' import { TouchbarService } from './services/touchbar.service'
@ -51,6 +52,7 @@ const PROVIDERS = [
HostAppService, HostAppService,
HotkeysService, HotkeysService,
LogService, LogService,
ShellIntegrationService,
TabRecoveryService, TabRecoveryService,
ThemesService, ThemesService,
TouchbarService, TouchbarService,

View File

@ -0,0 +1,85 @@
import * as path from 'path'
import * as fs from 'mz/fs'
import { exec } from 'mz/child_process'
import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service'
import { HostAppService, Platform } from './hostApp.service'
let Registry = null
try {
Registry = require('winreg')
} catch (_) { } // tslint:disable-line no-empty
@Injectable()
export class ShellIntegrationService {
private automatorWorkflows = ['Open Terminus here.workflow', 'Paste path into Terminus.workflow']
private automatorWorkflowsLocation: string
private automatorWorkflowsDestination: string
private registryKeys = [
{
path: '\\Software\\Classes\\Directory\\Background\\shell\\Open Terminus here',
command: 'open "%V"'
},
{
path: '\\Software\\Classes\\*\\shell\\Paste path into Terminus',
command: 'paste "%V"'
},
]
constructor (
private electron: ElectronService,
private hostApp: HostAppService,
) {
if (this.hostApp.platform === Platform.macOS) {
this.automatorWorkflowsLocation = path.join(
path.dirname(path.dirname(this.electron.app.getPath('exe'))),
'Resources',
'extras',
'automator-workflows',
)
this.automatorWorkflowsDestination = path.join(process.env.HOME, 'Library', 'Services')
}
}
async isInstalled (): Promise<boolean> {
if (this.hostApp.platform === Platform.macOS) {
return await fs.exists(path.join(this.automatorWorkflowsDestination, this.automatorWorkflows[0]))
} else if (this.hostApp.platform === Platform.Windows) {
return await new Promise<boolean>(resolve => {
let reg = new Registry({ hive: Registry.HKCU, key: this.registryKeys[0].path, arch: 'x64' })
reg.keyExists((err, exists) => resolve(!err && exists))
})
}
return true
}
async install () {
if (this.hostApp.platform === Platform.macOS) {
for (let wf of this.automatorWorkflows) {
await exec(`cp -r "${this.automatorWorkflowsLocation}/${wf}" "${this.automatorWorkflowsDestination}"`)
}
} else if (this.hostApp.platform === Platform.Windows) {
for (let registryKey of this.registryKeys) {
let reg = new Registry({ hive: Registry.HKCU, key: registryKey.path, arch: 'x64' })
await new Promise(resolve => {
reg.set('Icon', Registry.REG_SZ, this.electron.app.getPath('exe'), () => {
reg.create(() => {
let cmd = new Registry({
hive: Registry.HKCU,
key: registryKey.path + '\\command',
arch: 'x64'
})
cmd.create(() => {
cmd.set(
'',
Registry.REG_SZ,
this.electron.app.getPath('exe') + ' ' + registryKey.command,
() => resolve()
)
})
})
})
})
}
}
}
}

View File

@ -549,6 +549,10 @@ verror@1.10.0:
core-util-is "1.0.2" core-util-is "1.0.2"
extsprintf "^1.2.0" extsprintf "^1.2.0"
winreg@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b"
winston@^2.4.0: winston@^2.4.0:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.0.tgz#808050b93d52661ed9fb6c26b3f0c826708b0aee" resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.0.tgz#808050b93d52661ed9fb6c26b3f0c826708b0aee"

View File

@ -19,11 +19,11 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
i.fa.fa-bug i.fa.fa-bug
span Report a problem span Report a problem
.form-line(*ngIf='!automatorWorkflowsInstalled') .form-line(*ngIf='!isShellIntegrationInstalled')
.header .header
.title Finder context menu items .title Shell integration
.description Allows quickly opening a terminal in the selected folder .description Allows quickly opening a terminal in the selected folder
button.btn.btn-primary((click)='installAutomatorWorkflows()') button.btn.btn-primary((click)='installShellIntegration()')
i.fa.fa-check i.fa.fa-check
span Install span Install

View File

@ -1,10 +1,19 @@
import * as yaml from 'js-yaml' import * as yaml from 'js-yaml'
import * as path from 'path'
import * as fs from 'mz/fs'
import { exec } from 'mz/child_process'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { Component, Inject, Input } from '@angular/core' import { Component, Inject, Input } from '@angular/core'
import { ElectronService, DockingService, ConfigService, IHotkeyDescription, HotkeyProvider, BaseTabComponent, Theme, HostAppService, Platform, HomeBaseService } from 'terminus-core' import {
ElectronService,
DockingService,
ConfigService,
IHotkeyDescription,
HotkeyProvider,
BaseTabComponent,
Theme,
HostAppService,
Platform,
HomeBaseService,
ShellIntegrationService
} from 'terminus-core'
import { SettingsTabProvider } from '../api' import { SettingsTabProvider } from '../api'
@ -24,11 +33,8 @@ export class SettingsTabComponent extends BaseTabComponent {
Platform = Platform Platform = Platform
configDefaults: any configDefaults: any
configFile: string configFile: string
automatorWorkflowsInstalled = false isShellIntegrationInstalled = false
private configSubscription: Subscription private configSubscription: Subscription
private automatorWorkflows = ['Open Terminus here.workflow', 'Paste path into Terminus.workflow']
private automatorWorkflowsLocation: string
private automatorWorkflowsDestination: string
constructor ( constructor (
public config: ConfigService, public config: ConfigService,
@ -36,6 +42,7 @@ export class SettingsTabComponent extends BaseTabComponent {
public docking: DockingService, public docking: DockingService,
public hostApp: HostAppService, public hostApp: HostAppService,
public homeBase: HomeBaseService, public homeBase: HomeBaseService,
public shellIntegration: ShellIntegrationService,
@Inject(HotkeyProvider) hotkeyProviders: HotkeyProvider[], @Inject(HotkeyProvider) hotkeyProviders: HotkeyProvider[],
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[], @Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
@Inject(Theme) public themes: Theme[], @Inject(Theme) public themes: Theme[],
@ -52,19 +59,10 @@ export class SettingsTabComponent extends BaseTabComponent {
this.configSubscription = config.changed$.subscribe(() => { this.configSubscription = config.changed$.subscribe(() => {
this.configFile = config.readRaw() this.configFile = config.readRaw()
}) })
this.automatorWorkflowsLocation = path.join(
path.dirname(path.dirname(this.electron.app.getPath('exe'))),
'Resources',
'extras',
'automator-workflows',
)
this.automatorWorkflowsDestination = path.join(process.env.HOME, 'Library', 'Services')
} }
async ngOnInit () { async ngOnInit () {
this.automatorWorkflowsInstalled = await fs.exists(path.join(this.automatorWorkflowsDestination, this.automatorWorkflows[0])) this.isShellIntegrationInstalled = await this.shellIntegration.isInstalled()
} }
getRecoveryToken (): any { getRecoveryToken (): any {
@ -96,10 +94,8 @@ export class SettingsTabComponent extends BaseTabComponent {
} }
} }
async installAutomatorWorkflows () { async installShellIntegration () {
for (let wf of this.automatorWorkflows) { await this.shellIntegration.install()
await exec(`cp -r "${this.automatorWorkflowsLocation}/${wf}" "${this.automatorWorkflowsDestination}"`) this.isShellIntegrationInstalled = true
}
this.automatorWorkflowsInstalled = true
} }
} }