mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 05:00:30 +08:00
use window frame
This commit is contained in:
parent
cf943948bf
commit
179c606d78
12
changelog.md
12
changelog.md
@ -1,13 +1,9 @@
|
|||||||
### Break Changes
|
### Break Changes
|
||||||
|
|
||||||
- YAML覆写语法有所变动,请更新后参考文档进行修改
|
- 1.2.x YAML覆写语法有所变动,请更新后参考文档进行修改
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
- YAML覆写功能支持对数组进行覆盖/前置/追加操作
|
- 支持使用外部编辑器打开文件
|
||||||
- 缓存代理组图标
|
- 允许禁用系统标题栏
|
||||||
|
- 重写连接页面
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
- 修复provider过期时间解析错误
|
|
||||||
- 修复订阅名称解析错误
|
|
||||||
|
|||||||
@ -103,7 +103,7 @@ app.whenReady().then(async () => {
|
|||||||
optimizer.watchWindowShortcuts(window)
|
optimizer.watchWindowShortcuts(window)
|
||||||
})
|
})
|
||||||
registerIpcMainHandlers()
|
registerIpcMainHandlers()
|
||||||
createWindow()
|
await createWindow()
|
||||||
await createTray()
|
await createTray()
|
||||||
await initShortcut()
|
await initShortcut()
|
||||||
app.on('activate', function () {
|
app.on('activate', function () {
|
||||||
@ -140,9 +140,9 @@ async function handleDeepLink(url: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createWindow(show = false): void {
|
export async function createWindow(): Promise<void> {
|
||||||
Menu.setApplicationMenu(null)
|
Menu.setApplicationMenu(null)
|
||||||
// Create the browser window.
|
const { useWindowFrame = true } = await getAppConfig()
|
||||||
const mainWindowState = windowStateKeeper({
|
const mainWindowState = windowStateKeeper({
|
||||||
defaultWidth: 800,
|
defaultWidth: 800,
|
||||||
defaultHeight: 600
|
defaultHeight: 600
|
||||||
@ -155,12 +155,12 @@ export function createWindow(show = false): void {
|
|||||||
x: mainWindowState.x,
|
x: mainWindowState.x,
|
||||||
y: mainWindowState.y,
|
y: mainWindowState.y,
|
||||||
show: false,
|
show: false,
|
||||||
frame: false,
|
frame: useWindowFrame,
|
||||||
titleBarStyle: 'hidden',
|
titleBarStyle: useWindowFrame ? 'default' : 'hidden',
|
||||||
titleBarOverlay: {
|
titleBarOverlay: useWindowFrame
|
||||||
color: '#00000000',
|
? false
|
||||||
symbolColor: '#8D8D8D',
|
: {
|
||||||
height: 48
|
height: 49
|
||||||
},
|
},
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
...(process.platform === 'linux' ? { icon: icon } : {}),
|
...(process.platform === 'linux' ? { icon: icon } : {}),
|
||||||
@ -173,7 +173,7 @@ export function createWindow(show = false): void {
|
|||||||
mainWindowState.manage(mainWindow)
|
mainWindowState.manage(mainWindow)
|
||||||
mainWindow.on('ready-to-show', async () => {
|
mainWindow.on('ready-to-show', async () => {
|
||||||
const { silentStart } = await getAppConfig()
|
const { silentStart } = await getAppConfig()
|
||||||
if (!silentStart || show) {
|
if (!silentStart) {
|
||||||
mainWindow?.show()
|
mainWindow?.show()
|
||||||
mainWindow?.focusOnWebView()
|
mainWindow?.focusOnWebView()
|
||||||
}
|
}
|
||||||
@ -210,7 +210,5 @@ export function showMainWindow(): void {
|
|||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
mainWindow.show()
|
mainWindow.show()
|
||||||
mainWindow.focusOnWebView()
|
mainWindow.focusOnWebView()
|
||||||
} else {
|
|
||||||
createWindow(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import {
|
|||||||
profileConfigPath,
|
profileConfigPath,
|
||||||
profilesDir
|
profilesDir
|
||||||
} from '../utils/dirs'
|
} from '../utils/dirs'
|
||||||
import { app } from 'electron'
|
|
||||||
|
|
||||||
export async function webdavBackup(): Promise<boolean> {
|
export async function webdavBackup(): Promise<boolean> {
|
||||||
const webdav = await import('webdav')
|
const webdav = await import('webdav')
|
||||||
@ -52,8 +51,6 @@ export async function webdavRestore(filename: string): Promise<void> {
|
|||||||
const zipData = await client.getFileContents(`mihomo-party/${filename}`)
|
const zipData = await client.getFileContents(`mihomo-party/${filename}`)
|
||||||
const zip = new AdmZip(zipData as Buffer)
|
const zip = new AdmZip(zipData as Buffer)
|
||||||
zip.extractAllTo(dataDir(), true)
|
zip.extractAllTo(dataDir(), true)
|
||||||
app.relaunch()
|
|
||||||
app.quit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listWebdavBackups(): Promise<string[]> {
|
export async function listWebdavBackups(): Promise<string[]> {
|
||||||
|
|||||||
@ -16,8 +16,6 @@ export async function setPortable(portable: boolean): Promise<void> {
|
|||||||
} else {
|
} else {
|
||||||
await rm(path.join(exeDir(), 'PORTABLE'))
|
await rm(path.join(exeDir(), 'PORTABLE'))
|
||||||
}
|
}
|
||||||
app.relaunch()
|
|
||||||
app.quit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dataDir(): string {
|
export function dataDir(): string {
|
||||||
|
|||||||
@ -63,6 +63,7 @@ import { listWebdavBackups, webdavBackup, webdavDelete, webdavRestore } from '..
|
|||||||
import { getInterfaces } from '../sys/interface'
|
import { getInterfaces } from '../sys/interface'
|
||||||
import { copyEnv } from '../resolve/tray'
|
import { copyEnv } from '../resolve/tray'
|
||||||
import { registerShortcut } from '../resolve/shortcut'
|
import { registerShortcut } from '../resolve/shortcut'
|
||||||
|
import { mainWindow } from '..'
|
||||||
|
|
||||||
function ipcErrorWrapper<T>( // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
function ipcErrorWrapper<T>( // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
fn: (...args: any[]) => Promise<T> // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
fn: (...args: any[]) => Promise<T> // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@ -178,11 +179,18 @@ export function registerIpcMainHandlers(): void {
|
|||||||
ipcMain.handle('setNativeTheme', (_e, theme) => {
|
ipcMain.handle('setNativeTheme', (_e, theme) => {
|
||||||
setNativeTheme(theme)
|
setNativeTheme(theme)
|
||||||
})
|
})
|
||||||
|
ipcMain.handle('setTitleBarOverlay', (_e, overlay) => {
|
||||||
|
mainWindow?.setTitleBarOverlay(overlay)
|
||||||
|
})
|
||||||
ipcMain.handle('openFile', (_e, type, id, ext) => openFile(type, id, ext))
|
ipcMain.handle('openFile', (_e, type, id, ext) => openFile(type, id, ext))
|
||||||
ipcMain.handle('copyEnv', ipcErrorWrapper(copyEnv))
|
ipcMain.handle('copyEnv', ipcErrorWrapper(copyEnv))
|
||||||
ipcMain.handle('alert', (_e, msg) => {
|
ipcMain.handle('alert', (_e, msg) => {
|
||||||
dialog.showErrorBox('Mihomo Party', msg)
|
dialog.showErrorBox('Mihomo Party', msg)
|
||||||
})
|
})
|
||||||
|
ipcMain.handle('relaunchApp', () => {
|
||||||
|
app.relaunch()
|
||||||
|
app.quit()
|
||||||
|
})
|
||||||
ipcMain.handle('quitApp', () => app.quit())
|
ipcMain.handle('quitApp', () => app.quit())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,9 @@ import MihomoCoreCard from '@renderer/components/sider/mihomo-core-card'
|
|||||||
import ResourceCard from '@renderer/components/sider/resource-card'
|
import ResourceCard from '@renderer/components/sider/resource-card'
|
||||||
import UpdaterButton from '@renderer/components/updater/updater-button'
|
import UpdaterButton from '@renderer/components/updater/updater-button'
|
||||||
import { useAppConfig } from './hooks/use-app-config'
|
import { useAppConfig } from './hooks/use-app-config'
|
||||||
import { setNativeTheme } from './utils/ipc'
|
import { setNativeTheme, setTitleBarOverlay } from './utils/ipc'
|
||||||
|
import { platform } from './utils/init'
|
||||||
|
import { TitleBarOverlayOptions } from 'electron'
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
const { appConfig, patchAppConfig } = useAppConfig()
|
const { appConfig, patchAppConfig } = useAppConfig()
|
||||||
@ -36,6 +38,7 @@ const App: React.FC = () => {
|
|||||||
appTheme = 'system',
|
appTheme = 'system',
|
||||||
controlDns = true,
|
controlDns = true,
|
||||||
controlSniff = true,
|
controlSniff = true,
|
||||||
|
useWindowFrame = true,
|
||||||
siderOrder = [
|
siderOrder = [
|
||||||
'sysproxy',
|
'sysproxy',
|
||||||
'tun',
|
'tun',
|
||||||
@ -53,7 +56,7 @@ const App: React.FC = () => {
|
|||||||
} = appConfig || {}
|
} = appConfig || {}
|
||||||
const [order, setOrder] = useState(siderOrder)
|
const [order, setOrder] = useState(siderOrder)
|
||||||
const sensors = useSensors(useSensor(PointerSensor))
|
const sensors = useSensors(useSensor(PointerSensor))
|
||||||
const { setTheme } = useTheme()
|
const { setTheme, systemTheme } = useTheme()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const page = useRoutes(routes)
|
const page = useRoutes(routes)
|
||||||
@ -71,6 +74,30 @@ const App: React.FC = () => {
|
|||||||
setNativeTheme('dark')
|
setNativeTheme('dark')
|
||||||
}
|
}
|
||||||
setTheme(appTheme)
|
setTheme(appTheme)
|
||||||
|
if (!useWindowFrame) {
|
||||||
|
let theme = appTheme as string
|
||||||
|
if (appTheme === 'system') {
|
||||||
|
theme = systemTheme || 'light'
|
||||||
|
}
|
||||||
|
const options = { height: 48 } as TitleBarOverlayOptions
|
||||||
|
try {
|
||||||
|
if (platform !== 'darwin') {
|
||||||
|
if (theme.includes('light')) {
|
||||||
|
options.color = '#FFFFFF'
|
||||||
|
options.symbolColor = '#000000'
|
||||||
|
} else if (theme.includes('dark')) {
|
||||||
|
options.color = '#000000'
|
||||||
|
options.symbolColor = '#FFFFFF'
|
||||||
|
} else {
|
||||||
|
options.color = '#18181b'
|
||||||
|
options.symbolColor = '#FFFFFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTitleBarOverlay(options)
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
}, [appTheme])
|
}, [appTheme])
|
||||||
|
|
||||||
const onDragEnd = async (event: DragEndEvent): Promise<void> => {
|
const onDragEnd = async (event: DragEndEvent): Promise<void> => {
|
||||||
@ -108,7 +135,11 @@ const App: React.FC = () => {
|
|||||||
<div className="side w-[250px] h-full overflow-y-auto no-scrollbar">
|
<div className="side w-[250px] h-full overflow-y-auto no-scrollbar">
|
||||||
<div className="app-drag sticky top-0 z-40 backdrop-blur bg-background/40 h-[49px]">
|
<div className="app-drag sticky top-0 z-40 backdrop-blur bg-background/40 h-[49px]">
|
||||||
<div className="flex justify-between p-2">
|
<div className="flex justify-between p-2">
|
||||||
<h3 className="text-lg font-bold leading-[32px]">Mihomo Party</h3>
|
<h3
|
||||||
|
className={`text-lg font-bold leading-[32px] ${!useWindowFrame && platform === 'darwin' ? 'invisible' : ''}`}
|
||||||
|
>
|
||||||
|
Mihomo Party
|
||||||
|
</h3>
|
||||||
<UpdaterButton />
|
<UpdaterButton />
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { Divider } from '@nextui-org/react'
|
import { Divider } from '@nextui-org/react'
|
||||||
import React, { forwardRef, useImperativeHandle, useRef } from 'react'
|
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||||
|
import { platform } from '@renderer/utils/init'
|
||||||
|
import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: React.ReactNode
|
title?: React.ReactNode
|
||||||
header?: React.ReactNode
|
header?: React.ReactNode
|
||||||
@ -8,22 +10,42 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const BasePage = forwardRef<HTMLDivElement, Props>((props, ref) => {
|
const BasePage = forwardRef<HTMLDivElement, Props>((props, ref) => {
|
||||||
|
const { appConfig } = useAppConfig()
|
||||||
|
const { useWindowFrame = true } = appConfig || {}
|
||||||
|
const [overlayWidth, setOverlayWidth] = React.useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (platform !== 'darwin' && !useWindowFrame) {
|
||||||
|
try {
|
||||||
|
// @ts-ignore windowControlsOverlay
|
||||||
|
const windowControlsOverlay = window.navigator.windowControlsOverlay
|
||||||
|
setOverlayWidth(window.innerWidth - windowControlsOverlay.getTitlebarAreaRect().width)
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const contentRef = useRef<HTMLDivElement>(null)
|
const contentRef = useRef<HTMLDivElement>(null)
|
||||||
useImperativeHandle(ref, () => {
|
useImperativeHandle(ref, () => {
|
||||||
return contentRef.current as HTMLDivElement
|
return contentRef.current as HTMLDivElement
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={contentRef} className="w-full h-full overflow-y-auto custom-scrollbar">
|
<div ref={contentRef} className="w-full h-full">
|
||||||
<div className="sticky top-0 z-40 h-[49px] w-full backdrop-blur bg-background/40">
|
<div className="sticky top-0 z-40 h-[49px] w-full bg-background">
|
||||||
<div className="app-drag p-2 flex justify-between h-[48px]">
|
<div className="app-drag p-2 flex justify-between h-[48px]">
|
||||||
<div className="title h-full text-lg leading-[32px]">{props.title}</div>
|
<div className="title h-full text-lg leading-[32px]">{props.title}</div>
|
||||||
<div className="header h-full mr-[130px]">{props.header}</div>
|
<div style={{ marginRight: overlayWidth }} className="header h-full">
|
||||||
|
{props.header}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
</div>
|
</div>
|
||||||
<div className="content">{props.children}</div>
|
<div className="content h-[calc(100vh-49px)] overflow-y-auto custom-scrollbar">
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import {
|
|||||||
disableAutoRun,
|
disableAutoRun,
|
||||||
enableAutoRun,
|
enableAutoRun,
|
||||||
isPortable,
|
isPortable,
|
||||||
|
relaunchApp,
|
||||||
restartCore,
|
restartCore,
|
||||||
setNativeTheme,
|
|
||||||
setPortable
|
setPortable
|
||||||
} from '@renderer/utils/ipc'
|
} from '@renderer/utils/ipc'
|
||||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||||
@ -28,6 +28,7 @@ const GeneralConfig: React.FC = () => {
|
|||||||
useDockIcon = true,
|
useDockIcon = true,
|
||||||
showTraffic = true,
|
showTraffic = true,
|
||||||
proxyInTray = true,
|
proxyInTray = true,
|
||||||
|
useWindowFrame = true,
|
||||||
envType = platform === 'win32' ? 'powershell' : 'bash',
|
envType = platform === 'win32' ? 'powershell' : 'bash',
|
||||||
autoCheckUpdate,
|
autoCheckUpdate,
|
||||||
appTheme = 'system'
|
appTheme = 'system'
|
||||||
@ -43,13 +44,6 @@ const GeneralConfig: React.FC = () => {
|
|||||||
themeStr += `-${color}`
|
themeStr += `-${color}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (themeStr.includes('light')) {
|
|
||||||
setNativeTheme('light')
|
|
||||||
} else if (themeStr === 'system') {
|
|
||||||
setNativeTheme('system')
|
|
||||||
} else {
|
|
||||||
setNativeTheme('dark')
|
|
||||||
}
|
|
||||||
setTheme(themeStr)
|
setTheme(themeStr)
|
||||||
patchAppConfig({ appTheme: themeStr as AppTheme })
|
patchAppConfig({ appTheme: themeStr as AppTheme })
|
||||||
} else {
|
} else {
|
||||||
@ -172,6 +166,7 @@ const GeneralConfig: React.FC = () => {
|
|||||||
onSelectionChange={async (v) => {
|
onSelectionChange={async (v) => {
|
||||||
try {
|
try {
|
||||||
await setPortable(v.currentKey === 'portable')
|
await setPortable(v.currentKey === 'portable')
|
||||||
|
await relaunchApp()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
alert(e)
|
||||||
} finally {
|
} finally {
|
||||||
@ -184,6 +179,16 @@ const GeneralConfig: React.FC = () => {
|
|||||||
</Select>
|
</Select>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
)}
|
)}
|
||||||
|
<SettingItem title="使用系统标题栏" divider>
|
||||||
|
<Switch
|
||||||
|
size="sm"
|
||||||
|
isSelected={useWindowFrame}
|
||||||
|
onValueChange={async (v) => {
|
||||||
|
await patchAppConfig({ useWindowFrame: v })
|
||||||
|
await relaunchApp()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
<SettingItem title="背景色" divider={appTheme !== 'system'}>
|
<SettingItem title="背景色" divider={appTheme !== 'system'}>
|
||||||
<Tabs
|
<Tabs
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react'
|
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react'
|
||||||
import { webdavDelete, webdavRestore } from '@renderer/utils/ipc'
|
import { relaunchApp, webdavDelete, webdavRestore } from '@renderer/utils/ipc'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { MdDeleteForever } from 'react-icons/md'
|
import { MdDeleteForever } from 'react-icons/md'
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -36,6 +36,7 @@ const WebdavRestoreModal: React.FC<Props> = (props) => {
|
|||||||
setRestoring(true)
|
setRestoring(true)
|
||||||
try {
|
try {
|
||||||
await webdavRestore(filename)
|
await webdavRestore(filename)
|
||||||
|
await relaunchApp()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(`恢复失败: ${e}`)
|
alert(`恢复失败: ${e}`)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -123,7 +123,7 @@ const Connections: React.FC = () => {
|
|||||||
{isDetailModalOpen && selected && (
|
{isDetailModalOpen && selected && (
|
||||||
<ConnectionDetailModal onClose={() => setIsDetailModalOpen(false)} connection={selected} />
|
<ConnectionDetailModal onClose={() => setIsDetailModalOpen(false)} connection={selected} />
|
||||||
)}
|
)}
|
||||||
<div className="overflow-x-auto sticky top-[49px] z-40">
|
<div className="overflow-x-auto sticky top-0 z-40">
|
||||||
<div className="flex p-2 gap-2">
|
<div className="flex p-2 gap-2">
|
||||||
<Input
|
<Input
|
||||||
variant="flat"
|
variant="flat"
|
||||||
|
|||||||
@ -45,7 +45,7 @@ const Logs: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<BasePage title="实时日志">
|
<BasePage title="实时日志">
|
||||||
<div className="sticky top-[49px] z-40">
|
<div className="sticky top-0 z-40">
|
||||||
<div className="w-full flex p-2">
|
<div className="w-full flex p-2">
|
||||||
<Input
|
<Input
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@ -134,7 +134,7 @@ const Override: React.FC = () => {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="sticky top-[49px] z-40 backdrop-blur bg-background/40">
|
<div className="sticky top-0 z-40 bg-background">
|
||||||
<div className="flex p-2">
|
<div className="flex p-2">
|
||||||
<Input
|
<Input
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@ -124,10 +124,9 @@ const Profiles: React.FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="sticky top-[49px] z-40 backdrop-blur bg-background/40">
|
<div className="sticky top-0 z-40 bg-background">
|
||||||
<div className="flex p-2">
|
<div className="flex p-2">
|
||||||
<Input
|
<Input
|
||||||
// variant="bordered"
|
|
||||||
size="sm"
|
size="sm"
|
||||||
value={url}
|
value={url}
|
||||||
onValueChange={setUrl}
|
onValueChange={setUrl}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ const Rules: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<BasePage title="分流规则">
|
<BasePage title="分流规则">
|
||||||
<div className="sticky top-[49px] z-40">
|
<div className="sticky top-0 z-40">
|
||||||
<div className="flex p-2">
|
<div className="flex p-2">
|
||||||
<Input
|
<Input
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { TitleBarOverlayOptions } from 'electron'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function ipcErrorWrapper(response: any): any {
|
function ipcErrorWrapper(response: any): any {
|
||||||
if (typeof response === 'object' && 'invokeError' in response) {
|
if (typeof response === 'object' && 'invokeError' in response) {
|
||||||
@ -289,6 +291,14 @@ export async function webdavDelete(filename: string): Promise<void> {
|
|||||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('webdavDelete', filename))
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('webdavDelete', filename))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setTitleBarOverlay(overlay: TitleBarOverlayOptions): Promise<void> {
|
||||||
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setTitleBarOverlay', overlay))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function relaunchApp(): Promise<void> {
|
||||||
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('relaunchApp'))
|
||||||
|
}
|
||||||
|
|
||||||
export async function quitApp(): Promise<void> {
|
export async function quitApp(): Promise<void> {
|
||||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('quitApp'))
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('quitApp'))
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/shared/types.d.ts
vendored
1
src/shared/types.d.ts
vendored
@ -217,6 +217,7 @@ interface IAppConfig {
|
|||||||
proxyDisplayOrder: 'default' | 'delay' | 'name'
|
proxyDisplayOrder: 'default' | 'delay' | 'name'
|
||||||
envType?: 'bash' | 'cmd' | 'powershell'
|
envType?: 'bash' | 'cmd' | 'powershell'
|
||||||
proxyCols: 'auto' | '1' | '2' | '3' | '4'
|
proxyCols: 'auto' | '1' | '2' | '3' | '4'
|
||||||
|
useWindowFrame: boolean
|
||||||
proxyInTray: boolean
|
proxyInTray: boolean
|
||||||
siderOrder: string[]
|
siderOrder: string[]
|
||||||
appTheme: AppTheme
|
appTheme: AppTheme
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user