Skip to content

Commit

Permalink
support floating window
Browse files Browse the repository at this point in the history
  • Loading branch information
pompurin404 committed Oct 5, 2024
1 parent 23c1754 commit 6ff62d4
Show file tree
Hide file tree
Showing 19 changed files with 348 additions and 25 deletions.
5 changes: 1 addition & 4 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
### Breaking Changes

- 此版本修改了应用的显示名称,macOS用户可能无法自动更新,需要手动删除 `/Applications/mihomo-party.app`

### New Features

- 允许切换订阅卡片显示过期时间还是更新时间
- 添加悬浮窗功能,可以在设置中开启
8 changes: 8 additions & 0 deletions electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ export default defineConfig({
plugins: [externalizeDepsPlugin()]
},
renderer: {
build: {
rollupOptions: {
input: {
index: resolve('src/renderer/index.html'),
floating: resolve('src/renderer/floating.html')
}
}
},
resolve: {
alias: {
'@renderer': resolve('src/renderer/src')
Expand Down
2 changes: 2 additions & 0 deletions src/main/core/mihomoApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import WebSocket from 'ws'
import { tray } from '../resolve/tray'
import { calcTraffic } from '../utils/calc'
import { getRuntimeConfig } from './factory'
import { floatingWindow } from '../resolve/floatingWindow'

let axiosIns: AxiosInstance = null!
let mihomoTrafficWs: WebSocket | null = null
Expand Down Expand Up @@ -202,6 +203,7 @@ const mihomoTraffic = async (): Promise<void> => {
`${calcTraffic(json.down)}/s`.padStart(9)
)
}
floatingWindow?.webContents.send('mihomoTraffic', json)
} catch {
// ignore
}
Expand Down
15 changes: 13 additions & 2 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { existsSync, writeFileSync } from 'fs'
import { exePath, taskDir } from './utils/dirs'
import path from 'path'
import { startMonitor } from './resolve/trafficMonitor'
import { showFloatingWindow } from './resolve/floatingWindow'

let quitTimeout: NodeJS.Timeout | null = null
export let mainWindow: BrowserWindow | null = null
Expand Down Expand Up @@ -149,8 +150,12 @@ app.whenReady().then(async () => {
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
const { showFloatingWindow: showFloating = false } = await getAppConfig()
registerIpcMainHandlers()
await createWindow()
if (showFloating) {
showFloatingWindow()
}
await createTray()
await initShortcut()
app.on('activate', function () {
Expand Down Expand Up @@ -191,7 +196,8 @@ export async function createWindow(): Promise<void> {
const { useWindowFrame = false } = await getAppConfig()
const mainWindowState = windowStateKeeper({
defaultWidth: 800,
defaultHeight: 600
defaultHeight: 600,
file: 'window-state.json'
})
// https://github.com/electron/electron/issues/16521#issuecomment-582955104
Menu.setApplicationMenu(null)
Expand Down Expand Up @@ -269,7 +275,6 @@ export async function createWindow(): Promise<void> {
shell.openExternal(details.url)
return { action: 'deny' }
})

// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
Expand All @@ -288,3 +293,9 @@ export function showMainWindow(): void {
mainWindow.focusOnWebView()
}
}

export function closeMainWindow(): void {
if (mainWindow) {
mainWindow.close()
}
}
75 changes: 75 additions & 0 deletions src/main/resolve/floatingWindow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { is } from '@electron-toolkit/utils'
import { BrowserWindow, ipcMain } from 'electron'
import windowStateKeeper from 'electron-window-state'
import { join } from 'path'
import { getAppConfig } from '../config'
import { applyTheme } from './theme'
import { buildContextMenu } from './tray'

export let floatingWindow: BrowserWindow | null = null

async function createFloatingWindow(): Promise<void> {
const floatingWindowState = windowStateKeeper({
file: 'floating-window-state.json'
})
const { customTheme = 'default.css' } = await getAppConfig()
floatingWindow = new BrowserWindow({
width: 126,
height: 50,
x: floatingWindowState.x,
y: floatingWindowState.y,
show: false,
frame: false,
alwaysOnTop: true,
resizable: false,
transparent: true,
skipTaskbar: true,
minimizable: false,
maximizable: false,
closable: false,
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
spellcheck: false,
sandbox: false
}
})
floatingWindowState.manage(floatingWindow)
floatingWindow.on('ready-to-show', () => {
applyTheme(customTheme)
floatingWindow?.show()
})
floatingWindow.on('moved', () => {
if (floatingWindow) floatingWindowState.saveState(floatingWindow)
})
ipcMain.on('updateFloatingWindow', () => {
if (floatingWindow) {
floatingWindow?.webContents.send('controledMihomoConfigUpdated')
floatingWindow?.webContents.send('appConfigUpdated')
}
})
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
floatingWindow.loadURL(`${process.env['ELECTRON_RENDERER_URL']}/floating.html`)
} else {
floatingWindow.loadFile(join(__dirname, '../renderer/floating.html'))
}
}
export function showFloatingWindow(): void {
if (floatingWindow) {
floatingWindow.show()
} else {
createFloatingWindow()
}
}

export function closeFloatingWindow(): void {
if (floatingWindow) {
floatingWindow.close()
floatingWindow.destroy()
floatingWindow = null
}
}

export async function showContextMenu(): Promise<void> {
const menu = await buildContextMenu()
menu.popup()
}
24 changes: 22 additions & 2 deletions src/main/resolve/shortcut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { triggerSysProxy } from '../sys/sysproxy'
import { patchMihomoConfig } from '../core/mihomoApi'
import { quitWithoutCore, restartCore } from '../core/manager'
import { closeFloatingWindow, floatingWindow, showFloatingWindow } from './floatingWindow'

export async function registerShortcut(
oldShortcut: string,
Expand All @@ -31,6 +32,15 @@ export async function registerShortcut(
}
})
}
case 'showFloatingWindowShortcut': {
return globalShortcut.register(newShortcut, () => {
if (floatingWindow) {
closeFloatingWindow()
} else {
showFloatingWindow()
}
})
}
case 'triggerSysProxyShortcut': {
return globalShortcut.register(newShortcut, async () => {
const {
Expand All @@ -42,10 +52,11 @@ export async function registerShortcut(
new Notification({
title: `系统代理已${!enable ? '开启' : '关闭'}`
}).show()
mainWindow?.webContents.send('appConfigUpdated')
floatingWindow?.webContents.send('appConfigUpdated')
} catch {
// ignore
} finally {
mainWindow?.webContents.send('appConfigUpdated')
ipcMain.emit('updateTrayMenu')
}
})
Expand All @@ -64,10 +75,11 @@ export async function registerShortcut(
new Notification({
title: `虚拟网卡已${!enable ? '开启' : '关闭'}`
}).show()
mainWindow?.webContents.send('controledMihomoConfigUpdated')
floatingWindow?.webContents.send('appConfigUpdated')
} catch {
// ignore
} finally {
mainWindow?.webContents.send('controledMihomoConfigUpdated')
ipcMain.emit('updateTrayMenu')
}
})
Expand Down Expand Up @@ -122,6 +134,7 @@ export async function registerShortcut(

export async function initShortcut(): Promise<void> {
const {
showFloatingWindowShortcut,
showWindowShortcut,
triggerSysProxyShortcut,
triggerTunShortcut,
Expand All @@ -138,6 +151,13 @@ export async function initShortcut(): Promise<void> {
// ignore
}
}
if (showFloatingWindowShortcut) {
try {
await registerShortcut('', showFloatingWindowShortcut, 'showFloatingWindowShortcut')
} catch {
// ignore
}
}
if (triggerSysProxyShortcut) {
try {
await registerShortcut('', triggerSysProxyShortcut, 'triggerSysProxyShortcut')
Expand Down
14 changes: 11 additions & 3 deletions src/main/resolve/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import AdmZip from 'adm-zip'
import { getControledMihomoConfig } from '../config'
import { existsSync } from 'fs'
import { mainWindow } from '..'
import { floatingWindow } from './floatingWindow'

let insertedCSSKey: string | undefined = undefined
let insertedCSSKeyMain: string | undefined = undefined
let insertedCSSKeyFloating: string | undefined = undefined

export async function resolveThemes(): Promise<{ key: string; label: string }[]> {
const files = await readdir(themesDir())
Expand Down Expand Up @@ -67,6 +69,12 @@ export async function writeTheme(theme: string, css: string): Promise<void> {

export async function applyTheme(theme: string): Promise<void> {
const css = await readTheme(theme)
await mainWindow?.webContents.removeInsertedCSS(insertedCSSKey || '')
insertedCSSKey = await mainWindow?.webContents.insertCSS(css)
await mainWindow?.webContents.removeInsertedCSS(insertedCSSKeyMain || '')
insertedCSSKeyMain = await mainWindow?.webContents.insertCSS(css)
try {
await floatingWindow?.webContents.removeInsertedCSS(insertedCSSKeyFloating || '')
insertedCSSKeyFloating = await floatingWindow?.webContents.insertCSS(css)
} catch {
// ignore
}
}
48 changes: 35 additions & 13 deletions src/main/resolve/tray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,24 @@ import {
mihomoGroups,
patchMihomoConfig
} from '../core/mihomoApi'
import { mainWindow, showMainWindow } from '..'
import { closeMainWindow, mainWindow, showMainWindow } from '..'
import { app, clipboard, ipcMain, Menu, nativeImage, shell, Tray } from 'electron'
import { dataDir, logDir, mihomoCoreDir, mihomoWorkDir } from '../utils/dirs'
import { triggerSysProxy } from '../sys/sysproxy'
import { quitWithoutCore, restartCore } from '../core/manager'
import { closeFloatingWindow, floatingWindow, showFloatingWindow } from './floatingWindow'

export let tray: Tray | null = null

const buildContextMenu = async (): Promise<Menu> => {
export const buildContextMenu = async (): Promise<Menu> => {
const { mode, tun } = await getControledMihomoConfig()
const {
sysProxy,
envType = process.platform === 'win32' ? ['powershell'] : ['bash'],
autoCloseConnection,
proxyInTray = true,
triggerSysProxyShortcut = '',
showFloatingWindowShortcut = '',
showWindowShortcut = '',
triggerTunShortcut = '',
ruleModeShortcut = '',
Expand Down Expand Up @@ -90,6 +92,19 @@ const buildContextMenu = async (): Promise<Menu> => {
showMainWindow()
}
},
{
id: 'show-floating',
accelerator: showFloatingWindowShortcut,
label: floatingWindow ? '关闭悬浮窗' : '显示悬浮窗',
type: 'normal',
click: (): void => {
if (floatingWindow) {
closeFloatingWindow()
} else {
showFloatingWindow()
}
}
},
{
id: 'rule',
label: '规则模式',
Expand Down Expand Up @@ -140,10 +155,11 @@ const buildContextMenu = async (): Promise<Menu> => {
try {
await triggerSysProxy(enable)
await patchAppConfig({ sysProxy: { enable } })
mainWindow?.webContents.send('appConfigUpdated')
floatingWindow?.webContents.send('appConfigUpdated')
} catch (e) {
// ignore
} finally {
mainWindow?.webContents.send('appConfigUpdated')
ipcMain.emit('updateTrayMenu')
}
}
Expand All @@ -155,14 +171,20 @@ const buildContextMenu = async (): Promise<Menu> => {
checked: tun?.enable ?? false,
click: async (item): Promise<void> => {
const enable = item.checked
if (enable) {
await patchControledMihomoConfig({ tun: { enable }, dns: { enable: true } })
} else {
await patchControledMihomoConfig({ tun: { enable } })
try {
if (enable) {
await patchControledMihomoConfig({ tun: { enable }, dns: { enable: true } })
} else {
await patchControledMihomoConfig({ tun: { enable } })
}
mainWindow?.webContents.send('controledMihomoConfigUpdated')
floatingWindow?.webContents.send('controledMihomoConfigUpdated')
await restartCore()
} catch {
// ignore
} finally {
ipcMain.emit('updateTrayMenu')
}
mainWindow?.webContents.send('controledMihomoConfigUpdated')
await restartCore()
ipcMain.emit('updateTrayMenu')
}
},
...groupsMenu,
Expand Down Expand Up @@ -291,7 +313,7 @@ export async function createTray(): Promise<void> {
})
tray?.addListener('right-click', async () => {
if (mainWindow?.isVisible()) {
mainWindow?.close()
closeMainWindow()
} else {
showMainWindow()
}
Expand All @@ -303,7 +325,7 @@ export async function createTray(): Promise<void> {
if (process.platform === 'win32') {
tray?.addListener('click', () => {
if (mainWindow?.isVisible()) {
mainWindow?.close()
closeMainWindow()
} else {
showMainWindow()
}
Expand All @@ -315,7 +337,7 @@ export async function createTray(): Promise<void> {
if (process.platform === 'linux') {
tray?.addListener('click', () => {
if (mainWindow?.isVisible()) {
mainWindow?.close()
closeMainWindow()
} else {
showMainWindow()
}
Expand Down
Loading

0 comments on commit 6ff62d4

Please sign in to comment.