feat: add minapp window
This commit is contained in:
parent
e43f7f87ab
commit
1996e163c9
@ -7,7 +7,15 @@ export default defineConfig({
|
|||||||
plugins: [externalizeDepsPlugin()]
|
plugins: [externalizeDepsPlugin()]
|
||||||
},
|
},
|
||||||
preload: {
|
preload: {
|
||||||
plugins: [externalizeDepsPlugin()]
|
plugins: [externalizeDepsPlugin()],
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
index: resolve(__dirname, 'src/preload/index.ts'),
|
||||||
|
minapp: resolve(__dirname, 'src/preload/minapp.ts')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
renderer: {
|
renderer: {
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
65
resources/minapp.html
Normal file
65
resources/minapp.html
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>MinApp</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
height: 40px;
|
||||||
|
background-color: #303030;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
}
|
||||||
|
.header-right {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.header-left {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
.header-center {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 3px;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div class="header-left"></div>
|
||||||
|
<div class="header-center">MinApp</div>
|
||||||
|
<div class="header-right"></div>
|
||||||
|
</header>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -1,84 +1,12 @@
|
|||||||
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
|
import { electronApp, optimizer } from '@electron-toolkit/utils'
|
||||||
import * as Sentry from '@sentry/electron/main'
|
import * as Sentry from '@sentry/electron/main'
|
||||||
import { app, BrowserWindow, ipcMain, Menu, MenuItem, session, shell } from 'electron'
|
import { app, BrowserWindow, ipcMain, session, shell } from 'electron'
|
||||||
import installExtension, { REDUX_DEVTOOLS } from 'electron-devtools-installer'
|
import installExtension, { REDUX_DEVTOOLS } from 'electron-devtools-installer'
|
||||||
import windowStateKeeper from 'electron-window-state'
|
|
||||||
import { join } from 'path'
|
|
||||||
|
|
||||||
import icon from '../../build/icon.png?asset'
|
|
||||||
import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config'
|
import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config'
|
||||||
import { saveFile } from './event'
|
import { saveFile } from './event'
|
||||||
import AppUpdater from './updater'
|
import AppUpdater from './updater'
|
||||||
|
import { createMainWindow, createMinappWindow } from './window'
|
||||||
function createWindow() {
|
|
||||||
// Load the previous state with fallback to defaults
|
|
||||||
const mainWindowState = windowStateKeeper({
|
|
||||||
defaultWidth: 1080,
|
|
||||||
defaultHeight: 670
|
|
||||||
})
|
|
||||||
|
|
||||||
const theme = appConfig.get('theme') || 'light'
|
|
||||||
|
|
||||||
// Create the browser window.
|
|
||||||
const mainWindow = new BrowserWindow({
|
|
||||||
x: mainWindowState.x,
|
|
||||||
y: mainWindowState.y,
|
|
||||||
width: mainWindowState.width,
|
|
||||||
height: mainWindowState.height,
|
|
||||||
minWidth: 1080,
|
|
||||||
minHeight: 600,
|
|
||||||
show: true,
|
|
||||||
autoHideMenuBar: true,
|
|
||||||
transparent: process.platform === 'darwin',
|
|
||||||
vibrancy: 'fullscreen-ui',
|
|
||||||
titleBarStyle: 'hidden',
|
|
||||||
titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight,
|
|
||||||
trafficLightPosition: { x: 8, y: 12 },
|
|
||||||
...(process.platform === 'linux' ? { icon } : {}),
|
|
||||||
webPreferences: {
|
|
||||||
preload: join(__dirname, '../preload/index.js'),
|
|
||||||
sandbox: false,
|
|
||||||
webSecurity: false
|
|
||||||
// devTools: !app.isPackaged,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
mainWindowState.manage(mainWindow)
|
|
||||||
|
|
||||||
mainWindow.webContents.on('context-menu', () => {
|
|
||||||
const menu = new Menu()
|
|
||||||
menu.append(new MenuItem({ label: '复制', role: 'copy', sublabel: '⌘ + C' }))
|
|
||||||
menu.append(new MenuItem({ label: '粘贴', role: 'paste', sublabel: '⌘ + V' }))
|
|
||||||
menu.append(new MenuItem({ label: '剪切', role: 'cut', sublabel: '⌘ + X' }))
|
|
||||||
menu.append(new MenuItem({ type: 'separator' }))
|
|
||||||
menu.append(new MenuItem({ label: '全选', role: 'selectAll', sublabel: '⌘ + A' }))
|
|
||||||
menu.popup()
|
|
||||||
})
|
|
||||||
|
|
||||||
mainWindow.webContents.on('will-navigate', (event, url) => {
|
|
||||||
event.preventDefault()
|
|
||||||
shell.openExternal(url)
|
|
||||||
})
|
|
||||||
|
|
||||||
mainWindow.on('ready-to-show', () => {
|
|
||||||
mainWindow.show()
|
|
||||||
})
|
|
||||||
|
|
||||||
mainWindow.webContents.setWindowOpenHandler((details) => {
|
|
||||||
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']) {
|
|
||||||
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
|
||||||
} else {
|
|
||||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
|
||||||
}
|
|
||||||
|
|
||||||
return mainWindow
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method will be called when Electron has finished
|
// This method will be called when Electron has finished
|
||||||
// initialization and is ready to create browser windows.
|
// initialization and is ready to create browser windows.
|
||||||
@ -97,10 +25,10 @@ app.whenReady().then(() => {
|
|||||||
app.on('activate', function () {
|
app.on('activate', function () {
|
||||||
// On macOS it's common to re-create a window in the app when the
|
// On macOS it's common to re-create a window in the app when the
|
||||||
// dock icon is clicked and there are no other windows open.
|
// dock icon is clicked and there are no other windows open.
|
||||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
if (BrowserWindow.getAllWindows().length === 0) createMainWindow()
|
||||||
})
|
})
|
||||||
|
|
||||||
const mainWindow = createWindow()
|
const mainWindow = createMainWindow()
|
||||||
|
|
||||||
const { autoUpdater } = new AppUpdater(mainWindow)
|
const { autoUpdater } = new AppUpdater(mainWindow)
|
||||||
|
|
||||||
@ -121,6 +49,8 @@ app.whenReady().then(() => {
|
|||||||
|
|
||||||
ipcMain.handle('save-file', saveFile)
|
ipcMain.handle('save-file', saveFile)
|
||||||
|
|
||||||
|
ipcMain.handle('minapp', (_, url: string) => createMinappWindow(url))
|
||||||
|
|
||||||
ipcMain.handle('set-theme', (_, theme: 'light' | 'dark') => {
|
ipcMain.handle('set-theme', (_, theme: 'light' | 'dark') => {
|
||||||
appConfig.set('theme', theme)
|
appConfig.set('theme', theme)
|
||||||
mainWindow?.setTitleBarOverlay &&
|
mainWindow?.setTitleBarOverlay &&
|
||||||
|
|||||||
115
src/main/window.ts
Normal file
115
src/main/window.ts
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import { is } from '@electron-toolkit/utils'
|
||||||
|
import { app, BrowserView, BrowserWindow, Menu, MenuItem, shell } from 'electron'
|
||||||
|
import windowStateKeeper from 'electron-window-state'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
import icon from '../../build/icon.png?asset'
|
||||||
|
import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config'
|
||||||
|
|
||||||
|
export function createMainWindow() {
|
||||||
|
// Load the previous state with fallback to defaults
|
||||||
|
const mainWindowState = windowStateKeeper({
|
||||||
|
defaultWidth: 1080,
|
||||||
|
defaultHeight: 670
|
||||||
|
})
|
||||||
|
|
||||||
|
const theme = appConfig.get('theme') || 'light'
|
||||||
|
|
||||||
|
// Create the browser window.
|
||||||
|
const mainWindow = new BrowserWindow({
|
||||||
|
x: mainWindowState.x,
|
||||||
|
y: mainWindowState.y,
|
||||||
|
width: mainWindowState.width,
|
||||||
|
height: mainWindowState.height,
|
||||||
|
minWidth: 1080,
|
||||||
|
minHeight: 600,
|
||||||
|
show: true,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
transparent: process.platform === 'darwin',
|
||||||
|
vibrancy: 'fullscreen-ui',
|
||||||
|
titleBarStyle: 'hidden',
|
||||||
|
titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight,
|
||||||
|
trafficLightPosition: { x: 8, y: 12 },
|
||||||
|
...(process.platform === 'linux' ? { icon } : {}),
|
||||||
|
webPreferences: {
|
||||||
|
preload: join(__dirname, '../preload/index.js'),
|
||||||
|
sandbox: false,
|
||||||
|
webSecurity: false
|
||||||
|
// devTools: !app.isPackaged,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mainWindowState.manage(mainWindow)
|
||||||
|
|
||||||
|
mainWindow.webContents.on('context-menu', () => {
|
||||||
|
const menu = new Menu()
|
||||||
|
menu.append(new MenuItem({ label: '复制', role: 'copy', sublabel: '⌘ + C' }))
|
||||||
|
menu.append(new MenuItem({ label: '粘贴', role: 'paste', sublabel: '⌘ + V' }))
|
||||||
|
menu.append(new MenuItem({ label: '剪切', role: 'cut', sublabel: '⌘ + X' }))
|
||||||
|
menu.append(new MenuItem({ type: 'separator' }))
|
||||||
|
menu.append(new MenuItem({ label: '全选', role: 'selectAll', sublabel: '⌘ + A' }))
|
||||||
|
menu.popup()
|
||||||
|
})
|
||||||
|
|
||||||
|
mainWindow.on('ready-to-show', () => {
|
||||||
|
mainWindow.show()
|
||||||
|
})
|
||||||
|
|
||||||
|
mainWindow.webContents.on('will-navigate', (event, url) => {
|
||||||
|
event.preventDefault()
|
||||||
|
shell.openExternal(url)
|
||||||
|
})
|
||||||
|
|
||||||
|
mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||||
|
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']) {
|
||||||
|
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
||||||
|
} else {
|
||||||
|
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return mainWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMinappWindow(url) {
|
||||||
|
const width = 500
|
||||||
|
const height = 800
|
||||||
|
const headerHeight = 40
|
||||||
|
|
||||||
|
const minappWindow = new BrowserWindow({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
alwaysOnTop: true,
|
||||||
|
titleBarOverlay: titleBarOverlayDark,
|
||||||
|
titleBarStyle: 'hidden',
|
||||||
|
webPreferences: {
|
||||||
|
preload: join(__dirname, '../preload/minapp.js'),
|
||||||
|
sandbox: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
minappWindow.loadFile(app.getAppPath() + '/resources/minapp.html')
|
||||||
|
|
||||||
|
const view = new BrowserView()
|
||||||
|
|
||||||
|
minappWindow.setBrowserView(view)
|
||||||
|
view.setBounds({ x: 0, y: headerHeight, width, height: height - headerHeight })
|
||||||
|
view.webContents.loadURL(url)
|
||||||
|
|
||||||
|
minappWindow.on('resize', () => {
|
||||||
|
view.setBounds({
|
||||||
|
x: 0,
|
||||||
|
y: headerHeight,
|
||||||
|
width: minappWindow.getBounds().width,
|
||||||
|
height: minappWindow.getBounds().height - headerHeight
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return minappWindow
|
||||||
|
}
|
||||||
1
src/preload/index.d.ts
vendored
1
src/preload/index.d.ts
vendored
@ -14,6 +14,7 @@ declare global {
|
|||||||
setProxy: (proxy: string | undefined) => void
|
setProxy: (proxy: string | undefined) => void
|
||||||
saveFile: (path: string, content: string) => void
|
saveFile: (path: string, content: string) => void
|
||||||
setTheme: (theme: 'light' | 'dark') => void
|
setTheme: (theme: 'light' | 'dark') => void
|
||||||
|
minApp: (url: string) => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,8 @@ const api = {
|
|||||||
openWebsite: (url: string) => ipcRenderer.invoke('open-website', url),
|
openWebsite: (url: string) => ipcRenderer.invoke('open-website', url),
|
||||||
setProxy: (proxy: string) => ipcRenderer.invoke('set-proxy', proxy),
|
setProxy: (proxy: string) => ipcRenderer.invoke('set-proxy', proxy),
|
||||||
saveFile: (path: string, content: string) => ipcRenderer.invoke('save-file', path, content),
|
saveFile: (path: string, content: string) => ipcRenderer.invoke('save-file', path, content),
|
||||||
setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('set-theme', theme)
|
setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('set-theme', theme),
|
||||||
|
minApp: (url: string) => ipcRenderer.invoke('minapp', url)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use `contextBridge` APIs to expose Electron APIs to
|
// Use `contextBridge` APIs to expose Electron APIs to
|
||||||
|
|||||||
14
src/preload/minapp.ts
Normal file
14
src/preload/minapp.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { contextBridge } from 'electron'
|
||||||
|
|
||||||
|
const api = {}
|
||||||
|
|
||||||
|
if (process.contextIsolated) {
|
||||||
|
try {
|
||||||
|
contextBridge.exposeInMainWorld('api', api)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// @ts-ignore (define in dts)
|
||||||
|
window.api = api
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user