diff --git a/package.json b/package.json
index 9d9037f..c157f01 100644
--- a/package.json
+++ b/package.json
@@ -6,17 +6,22 @@
"author": "example.com",
"homepage": "https://electron-vite.org",
"scripts": {
- "dev": "electron-vite dev",
+ "dev": "electron-vite dev --sourcemap --remote-debugging-port=9000",
"format": "prettier --write .",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"typecheck": "tsc",
"start": "electron-vite preview",
"build": "electron-vite build && npm run typecheck",
+ "build-test": "electron-vite build --mode testing && npm run typecheck",
"postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir",
+ "build-test:unpack": "npm run build-test && electron-builder --dir",
"build:win": "npm run build && electron-builder --win",
- "build:mac": "electron-vite build && electron-builder --mac",
- "build:linux": "electron-vite build && electron-builder --linux"
+ "build-test:win": "npm run build-test && electron-builder --win",
+ "build:mac": "npm run build && electron-builder --mac",
+ "build-test:mac": "npm run build-test && electron-builder --mac",
+ "build:linux": "npm run build && electron-builder --linux",
+ "build-test:linux": "npm run build-test && electron-builder --linux"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
diff --git a/src/main/global.d.ts b/src/main/global.d.ts
new file mode 100644
index 0000000..3c22eaa
--- /dev/null
+++ b/src/main/global.d.ts
@@ -0,0 +1,13 @@
+type Platform = 'WEB' | 'DESKTOP' | 'ANDROID'
+
+interface ImportMetaEnv {
+ readonly VITE_PLATFORM: Platform
+ readonly VITE_PROTOCOL: string
+ readonly VITE_API_URL: string
+ readonly VITE_API_TOKEN_URL: string
+ readonly VITE_TURNSTILE_SITE_KEY: string
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv
+}
diff --git a/src/main/index.ts b/src/main/index.ts
index 9f762a8..c953f1a 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -1,11 +1,58 @@
-import { app, shell, BrowserWindow, ipcMain } from 'electron'
+import { app, shell, BrowserWindow, ipcMain, webContents } from 'electron'
import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/logo.ico?asset'
+import * as path from 'node:path'
+
+let mainWindow: BrowserWindow
+
+// Application singleton execution
+if (!app.requestSingleInstanceLock()) {
+ app.quit()
+}
+
+// Register protocol client
+const args: string[] = []
+if (!app.isPackaged) {
+ args.push(path.resolve(process.argv[1]))
+}
+args.push('--')
+app.setAsDefaultProtocolClient(import.meta.env.VITE_PROTOCOL, process.execPath, args)
+
+const handleArgv = (argv: string[]) => {
+ const prefix = `${import.meta.env.VITE_PROTOCOL}:`
+ const offset = app.isPackaged ? 1 : 2
+ const url = argv.find((arg, index) => index >= offset && arg.startsWith(prefix))
+ if (url) {
+ handleUrl(url)
+ }
+}
+
+const handleUrl = (url: string) => {
+ const { hostname, pathname } = new URL(url)
+ if (hostname === 'openurl') {
+ webContents.getAllWebContents().forEach((webContent) => {
+ webContent.send('open-url', pathname)
+ })
+ }
+}
+
+// Windows
+handleArgv(process.argv)
+app.on('second-instance', (_, argv) => {
+ if (process.platform === 'win32') {
+ handleArgv(argv)
+ }
+})
+
+// macOS
+app.on('open-url', (_, argv) => {
+ handleUrl(argv)
+})
function createWindow(): void {
// Create the browser window.
- const mainWindow = new BrowserWindow({
+ mainWindow = new BrowserWindow({
width: 900,
height: 670,
show: false,
diff --git a/src/preload/index.ts b/src/preload/index.ts
index 3e89029..f4c7828 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -9,15 +9,15 @@ const api = {}
// just add to the DOM global.
if (process.contextIsolated) {
try {
- contextBridge.exposeInMainWorld('electron', electronAPI)
+ contextBridge.exposeInMainWorld('electronAPI', electronAPI)
contextBridge.exposeInMainWorld('api', api)
- contextBridge.exposeInMainWorld('notification', Notification)
+ contextBridge.exposeInMainWorld('Notification', Notification)
} catch (error) {
console.error(error)
}
} else {
// @ts-expect-error (define in dts)
- window.electron = electronAPI
+ window.electronAPI = electronAPI
// @ts-expect-error (define in dts)
window.api = api
// @ts-expect-error (define in dts)
diff --git a/src/renderer/src/AuthRoute.tsx b/src/renderer/src/AuthRoute.tsx
index 0336fd1..81e81fa 100644
--- a/src/renderer/src/AuthRoute.tsx
+++ b/src/renderer/src/AuthRoute.tsx
@@ -3,6 +3,7 @@ import { getRedirectUrl } from '@/util/route'
import { getLoginStatus, getVerifyStatus_async } from '@/util/auth'
const AuthRoute = () => {
+ const navigate = useNavigate()
const [searchParams] = useSearchParams()
const matches = useMatches()
const lastMatch = matches.reduce((_, second) => second)
@@ -12,6 +13,12 @@ const AuthRoute = () => {
const isLogin = getLoginStatus()
const isVerify = getVerifyStatus_async()
+ useEffect(() => {
+ window.electronAPI.ipcRenderer.on('open-url', (_, url: string) => {
+ navigate(url)
+ })
+ }, [])
+
return useMemo(() => {
document.title = `${handle?.titlePrefix ?? ''}${
handle?.title ? handle?.title : PRODUCTION_NAME
diff --git a/src/renderer/src/electron.d.ts b/src/renderer/src/electron.d.ts
new file mode 100644
index 0000000..c8c1689
--- /dev/null
+++ b/src/renderer/src/electron.d.ts
@@ -0,0 +1,8 @@
+import { ElectronAPI } from "@electron-toolkit/preload";
+import { Notification } from "electron";
+
+declare global {
+ type _ElectronAPI = ElectronAPI
+
+ class _Notification extends Notification {}
+}
diff --git a/src/renderer/src/global.d.ts b/src/renderer/src/global.d.ts
index e67e298..1296c5e 100644
--- a/src/renderer/src/global.d.ts
+++ b/src/renderer/src/global.d.ts
@@ -1,10 +1,12 @@
///
+///
///
type Platform = 'WEB' | 'DESKTOP' | 'ANDROID'
interface ImportMetaEnv {
readonly VITE_PLATFORM: Platform
+ readonly VITE_PROTOCOL: string
readonly VITE_API_URL: string
readonly VITE_API_TOKEN_URL: string
readonly VITE_TURNSTILE_SITE_KEY: string
@@ -14,6 +16,11 @@ interface ImportMeta {
readonly env: ImportMetaEnv
}
+interface Window {
+ electronAPI: _ElectronAPI
+ Notification: typeof _Notification
+}
+
interface RouteJsonObject {
path: string
absolutePath: string