mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-20 02:18:01 +00:00
synchronize config saves between windows
This commit is contained in:
@@ -6,7 +6,7 @@ import * as path from 'path'
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import { Subject, throttleTime } from 'rxjs'
|
import { Subject, throttleTime } from 'rxjs'
|
||||||
|
|
||||||
import { loadConfig } from './config'
|
import { saveConfig } from './config'
|
||||||
import { Window, WindowOptions } from './window'
|
import { Window, WindowOptions } from './window'
|
||||||
import { pluginManager } from './pluginManager'
|
import { pluginManager } from './pluginManager'
|
||||||
import { PTYManager } from './pty'
|
import { PTYManager } from './pty'
|
||||||
@@ -23,10 +23,10 @@ export class Application {
|
|||||||
private windows: Window[] = []
|
private windows: Window[] = []
|
||||||
private globalHotkey$ = new Subject<void>()
|
private globalHotkey$ = new Subject<void>()
|
||||||
private quitRequested = false
|
private quitRequested = false
|
||||||
private configStore: any
|
|
||||||
userPluginsPath: string
|
userPluginsPath: string
|
||||||
|
|
||||||
constructor () {
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
|
constructor (private configStore: any) {
|
||||||
remote.initialize()
|
remote.initialize()
|
||||||
this.useBuiltinGraphics()
|
this.useBuiltinGraphics()
|
||||||
this.ptyManager.init(this)
|
this.ptyManager.init(this)
|
||||||
@@ -36,6 +36,10 @@ export class Application {
|
|||||||
this.configStore = config
|
this.configStore = config
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.on('app:save-config', (_event, data) => {
|
||||||
|
saveConfig(data)
|
||||||
|
})
|
||||||
|
|
||||||
ipcMain.on('app:register-global-hotkey', (_event, specs) => {
|
ipcMain.on('app:register-global-hotkey', (_event, specs) => {
|
||||||
globalShortcut.unregisterAll()
|
globalShortcut.unregisterAll()
|
||||||
for (const spec of specs) {
|
for (const spec of specs) {
|
||||||
@@ -63,7 +67,6 @@ export class Application {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.configStore = loadConfig()
|
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
app.commandLine.appendSwitch('no-sandbox')
|
app.commandLine.appendSwitch('no-sandbox')
|
||||||
if (((this.configStore.appearance || {}).opacity || 1) !== 1) {
|
if (((this.configStore.appearance || {}).opacity || 1) !== 1) {
|
||||||
@@ -111,7 +114,7 @@ export class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async newWindow (options?: WindowOptions): Promise<Window> {
|
async newWindow (options?: WindowOptions): Promise<Window> {
|
||||||
const window = new Window(this, options)
|
const window = new Window(this, this.configStore, options)
|
||||||
this.windows.push(window)
|
this.windows.push(window)
|
||||||
if (this.windows.length === 1){
|
if (this.windows.length === 1){
|
||||||
window.makeMain()
|
window.makeMain()
|
||||||
|
@@ -1,26 +1,47 @@
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'mz/fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as yaml from 'js-yaml'
|
import * as yaml from 'js-yaml'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import * as gracefulFS from 'graceful-fs'
|
||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
|
import { promisify } from 'util'
|
||||||
|
|
||||||
export function migrateConfig (): void {
|
export async function migrateConfig (): Promise<void> {
|
||||||
const configPath = path.join(app.getPath('userData'), 'config.yaml')
|
const configPath = path.join(app.getPath('userData'), 'config.yaml')
|
||||||
const legacyConfigPath = path.join(app.getPath('userData'), '../terminus', 'config.yaml')
|
const legacyConfigPath = path.join(app.getPath('userData'), '../terminus', 'config.yaml')
|
||||||
if (fs.existsSync(legacyConfigPath) && (
|
if (await fs.exists(legacyConfigPath) && (
|
||||||
!fs.existsSync(configPath) ||
|
!await fs.exists(configPath) ||
|
||||||
fs.statSync(configPath).mtime < fs.statSync(legacyConfigPath).mtime
|
(await fs.stat(configPath)).mtime < (await fs.stat(legacyConfigPath)).mtime
|
||||||
)) {
|
)) {
|
||||||
fs.writeFileSync(configPath, fs.readFileSync(legacyConfigPath))
|
await fs.writeFile(configPath, await fs.readFile(legacyConfigPath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadConfig (): any {
|
export async function loadConfig (): Promise<any> {
|
||||||
migrateConfig()
|
await migrateConfig()
|
||||||
|
|
||||||
const configPath = path.join(app.getPath('userData'), 'config.yaml')
|
const configPath = path.join(app.getPath('userData'), 'config.yaml')
|
||||||
if (fs.existsSync(configPath)) {
|
if (await fs.exists(configPath)) {
|
||||||
return yaml.load(fs.readFileSync(configPath, 'utf8'))
|
return yaml.load(await fs.readFile(configPath, 'utf8'))
|
||||||
} else {
|
} else {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const configPath = path.join(app.getPath('userData'), 'config.yaml')
|
||||||
|
let _configSaveInProgress = Promise.resolve()
|
||||||
|
|
||||||
|
async function _saveConfigInternal (content: string): Promise<void> {
|
||||||
|
const tempPath = configPath + '.new.' + uuidv4().toString()
|
||||||
|
await fs.writeFile(tempPath, content, 'utf8')
|
||||||
|
await fs.writeFile(configPath + '.backup', content, 'utf8')
|
||||||
|
await promisify(gracefulFS.rename)(tempPath, configPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveConfig (content: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await _configSaveInProgress
|
||||||
|
} catch { }
|
||||||
|
_configSaveInProgress = _saveConfigInternal(content)
|
||||||
|
await _configSaveInProgress
|
||||||
|
}
|
||||||
|
@@ -3,16 +3,20 @@ import './portable'
|
|||||||
import 'source-map-support/register'
|
import 'source-map-support/register'
|
||||||
import './sentry'
|
import './sentry'
|
||||||
import './lru'
|
import './lru'
|
||||||
import { app, ipcMain, Menu } from 'electron'
|
import { app, ipcMain, Menu, dialog } from 'electron'
|
||||||
import { parseArgs } from './cli'
|
import { parseArgs } from './cli'
|
||||||
import { Application } from './app'
|
import { Application } from './app'
|
||||||
import electronDebug = require('electron-debug')
|
import electronDebug = require('electron-debug')
|
||||||
|
import { loadConfig } from './config'
|
||||||
|
|
||||||
if (!process.env.TABBY_PLUGINS) {
|
if (!process.env.TABBY_PLUGINS) {
|
||||||
process.env.TABBY_PLUGINS = ''
|
process.env.TABBY_PLUGINS = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const application = new Application()
|
const argv = parseArgs(process.argv, process.cwd())
|
||||||
|
|
||||||
|
loadConfig().then(configStore => {
|
||||||
|
const application = new Application(configStore)
|
||||||
|
|
||||||
ipcMain.on('app:new-window', () => {
|
ipcMain.on('app:new-window', () => {
|
||||||
application.newWindow()
|
application.newWindow()
|
||||||
@@ -31,25 +35,10 @@ process.on('uncaughtException' as any, err => {
|
|||||||
application.broadcast('uncaughtException', err)
|
application.broadcast('uncaughtException', err)
|
||||||
})
|
})
|
||||||
|
|
||||||
app.on('second-instance', (_event, argv, cwd) => {
|
app.on('second-instance', (_event, newArgv, cwd) => {
|
||||||
application.handleSecondInstance(argv, cwd)
|
application.handleSecondInstance(newArgv, 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', async () => {
|
app.on('ready', async () => {
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
app.dock.setMenu(Menu.buildFromTemplate([
|
app.dock.setMenu(Menu.buildFromTemplate([
|
||||||
@@ -67,3 +56,19 @@ app.on('ready', async () => {
|
|||||||
await window.ready
|
await window.ready
|
||||||
window.passCliArguments(process.argv, process.cwd(), false)
|
window.passCliArguments(process.argv, process.cwd(), false)
|
||||||
})
|
})
|
||||||
|
}).catch(err => {
|
||||||
|
dialog.showErrorBox('Could not read config', err.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!app.requestSingleInstanceLock()) {
|
||||||
|
app.quit()
|
||||||
|
app.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv.d) {
|
||||||
|
electronDebug({
|
||||||
|
isEnabled: true,
|
||||||
|
showDevTools: true,
|
||||||
|
devToolsMode: 'undocked',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -11,7 +11,6 @@ import { compare as compareVersions } from 'compare-versions'
|
|||||||
|
|
||||||
import type { Application } from './app'
|
import type { Application } from './app'
|
||||||
import { parseArgs } from './cli'
|
import { parseArgs } from './cli'
|
||||||
import { loadConfig } from './config'
|
|
||||||
|
|
||||||
let DwmEnableBlurBehindWindow: any = null
|
let DwmEnableBlurBehindWindow: any = null
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
@@ -42,7 +41,6 @@ export class Window {
|
|||||||
private closing = false
|
private closing = false
|
||||||
private lastVibrancy: { enabled: boolean, type?: string } | null = null
|
private lastVibrancy: { enabled: boolean, type?: string } | null = null
|
||||||
private disableVibrancyWhileDragging = false
|
private disableVibrancyWhileDragging = false
|
||||||
private configStore: any
|
|
||||||
private touchBarControl: any
|
private touchBarControl: any
|
||||||
private isFluentVibrancy = false
|
private isFluentVibrancy = false
|
||||||
private dockHidden = false
|
private dockHidden = false
|
||||||
@@ -50,9 +48,8 @@ export class Window {
|
|||||||
get visible$ (): Observable<boolean> { return this.visible }
|
get visible$ (): Observable<boolean> { return this.visible }
|
||||||
get closed$ (): Observable<void> { return this.closed }
|
get closed$ (): Observable<void> { return this.closed }
|
||||||
|
|
||||||
constructor (private application: Application, options?: WindowOptions) {
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
this.configStore = loadConfig()
|
constructor (private application: Application, private configStore: any, options?: WindowOptions) {
|
||||||
|
|
||||||
options = options ?? {}
|
options = options ?? {}
|
||||||
|
|
||||||
this.windowConfig = new ElectronConfig({ name: 'window' })
|
this.windowConfig = new ElectronConfig({ name: 'window' })
|
||||||
|
@@ -65,6 +65,10 @@ export class ElectronHostAppService extends HostAppService {
|
|||||||
this.electron.ipcRenderer.send('app:config-change', configStore)
|
this.electron.ipcRenderer.send('app:config-change', configStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveConfig (data: string): void {
|
||||||
|
this.electron.ipcRenderer.send('app:save-config', data)
|
||||||
|
}
|
||||||
|
|
||||||
emitReady (): void {
|
emitReady (): void {
|
||||||
this.electron.ipcRenderer.send('app:ready')
|
this.electron.ipcRenderer.send('app:ready')
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,15 @@
|
|||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as fs from 'fs/promises'
|
import * as fs from 'fs/promises'
|
||||||
import * as gracefulFS from 'graceful-fs'
|
|
||||||
import * as fsSync from 'fs'
|
import * as fsSync from 'fs'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import { promisify } from 'util'
|
|
||||||
import promiseIpc, { RendererProcessType } from 'electron-promise-ipc'
|
import promiseIpc, { RendererProcessType } from 'electron-promise-ipc'
|
||||||
import { execFile } from 'mz/child_process'
|
import { execFile } from 'mz/child_process'
|
||||||
import { Injectable, NgZone } from '@angular/core'
|
import { Injectable, NgZone } from '@angular/core'
|
||||||
import { PlatformService, ClipboardContent, HostAppService, Platform, MenuItemOptions, MessageBoxOptions, MessageBoxResult, FileUpload, FileDownload, FileUploadOptions, wrapPromise, TranslateService } from 'tabby-core'
|
import { PlatformService, ClipboardContent, Platform, MenuItemOptions, MessageBoxOptions, MessageBoxResult, FileUpload, FileDownload, FileUploadOptions, wrapPromise, TranslateService } from 'tabby-core'
|
||||||
import { ElectronService } from '../services/electron.service'
|
import { ElectronService } from '../services/electron.service'
|
||||||
import { ElectronHostWindow } from './hostWindow.service'
|
import { ElectronHostWindow } from './hostWindow.service'
|
||||||
import { ShellIntegrationService } from './shellIntegration.service'
|
import { ShellIntegrationService } from './shellIntegration.service'
|
||||||
|
import { ElectronHostAppService } from './hostApp.service'
|
||||||
const fontManager = require('fontmanager-redux') // eslint-disable-line
|
const fontManager = require('fontmanager-redux') // eslint-disable-line
|
||||||
|
|
||||||
/* eslint-disable block-scoped-var */
|
/* eslint-disable block-scoped-var */
|
||||||
@@ -27,10 +25,9 @@ try {
|
|||||||
export class ElectronPlatformService extends PlatformService {
|
export class ElectronPlatformService extends PlatformService {
|
||||||
supportsWindowControls = true
|
supportsWindowControls = true
|
||||||
private configPath: string
|
private configPath: string
|
||||||
private _configSaveInProgress = Promise.resolve()
|
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private hostApp: HostAppService,
|
private hostApp: ElectronHostAppService,
|
||||||
private hostWindow: ElectronHostWindow,
|
private hostWindow: ElectronHostWindow,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
@@ -112,18 +109,7 @@ export class ElectronPlatformService extends PlatformService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async saveConfig (content: string): Promise<void> {
|
async saveConfig (content: string): Promise<void> {
|
||||||
try {
|
this.hostApp.saveConfig(content)
|
||||||
await this._configSaveInProgress
|
|
||||||
} catch { }
|
|
||||||
this._configSaveInProgress = this._saveConfigInternal(content)
|
|
||||||
await this._configSaveInProgress
|
|
||||||
}
|
|
||||||
|
|
||||||
async _saveConfigInternal (content: string): Promise<void> {
|
|
||||||
const tempPath = this.configPath + '.new.' + uuidv4().toString()
|
|
||||||
await fs.writeFile(tempPath, content, 'utf8')
|
|
||||||
await fs.writeFile(this.configPath + '.backup', content, 'utf8')
|
|
||||||
await promisify(gracefulFS.rename)(tempPath, this.configPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfigPath (): string|null {
|
getConfigPath (): string|null {
|
||||||
|
Reference in New Issue
Block a user