-
Notifications
You must be signed in to change notification settings - Fork 328
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add electron native webview render support #152
all website can be allow to visit in electron
- Loading branch information
1 parent
af16ebe
commit 146952d
Showing
10 changed files
with
357 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -222,7 +222,7 @@ | |
"electronmon": { | ||
"patterns": [ | ||
"!**/**", | ||
"src/main/*" | ||
"src/main/**/*" | ||
], | ||
"logLevel": "quiet" | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
/** | ||
* Fork from https://github.com/msgbyte/webbox/blob/main/src/main/webviewManager.ts | ||
*/ | ||
|
||
import { BrowserView, BrowserWindow, ipcMain, Rectangle } from 'electron'; | ||
import os from 'os'; | ||
import log from 'electron-log'; | ||
|
||
interface WebviewInfo { | ||
view: BrowserView; | ||
url: string; | ||
hidden: boolean; | ||
} | ||
|
||
const webviewMap = new Map<string, WebviewInfo>(); | ||
|
||
/** | ||
* fix rect into correct size | ||
*/ | ||
function fixRect(rect: Rectangle, isFullScreen: boolean): Rectangle { | ||
const xOffset = 1; | ||
const yOffset = !isFullScreen && os.platform() === 'darwin' ? 28 : 0; // add y axis offset in mac os if is not fullScreen | ||
|
||
return { | ||
x: Math.round(rect.x) + xOffset, | ||
y: Math.round(rect.y) + yOffset, | ||
width: Math.round(rect.width) - xOffset, | ||
height: Math.round(rect.height), | ||
}; | ||
} | ||
|
||
export function initWebviewManager(win: BrowserWindow) { | ||
ipcMain.on('$mount-webview', (e, info) => { | ||
if (!win) { | ||
log.info('[mount-webview]', 'cannot get mainWindow'); | ||
return; | ||
} | ||
|
||
log.info('[mount-webview] info:', info); | ||
|
||
const { key, url } = info; | ||
if (!url) { | ||
return; | ||
} | ||
|
||
if (webviewMap.has(key)) { | ||
const webview = webviewMap.get(key)!; | ||
win.setTopBrowserView(webview.view); | ||
webview.view.setBounds(fixRect(info.rect, win.isFullScreen())); | ||
if (webview.url !== url) { | ||
// url has been change. | ||
webview.view.webContents.loadURL(url); | ||
} | ||
return; | ||
} | ||
|
||
// hideAllWebview(); | ||
const view = new BrowserView({ | ||
webPreferences: { | ||
nodeIntegration: false, | ||
}, | ||
}); | ||
view.setBackgroundColor('#fff'); | ||
view.setBounds(fixRect(info.rect, win.isFullScreen())); | ||
view.webContents.loadURL(url); | ||
win.addBrowserView(view); | ||
webviewMap.set(key, { view, url, hidden: false }); | ||
}); | ||
|
||
ipcMain.on('$unmount-webview', (e, info) => { | ||
if (!win) { | ||
log.info('[unmount-webview]', 'cannot get mainWindow'); | ||
return; | ||
} | ||
|
||
log.info('[unmount-webview] info:', info); | ||
|
||
const { key } = info; | ||
const webview = webviewMap.get(key); | ||
if (webview) { | ||
win.removeBrowserView(webview.view); | ||
webviewMap.delete(key); | ||
} | ||
}); | ||
|
||
ipcMain.on('$update-webview-rect', (e, info) => { | ||
if (!win) { | ||
log.info('[update-webview-rect]', 'cannot get mainWindow'); | ||
return; | ||
} | ||
|
||
log.info('[update-webview-rect] info:', info); | ||
|
||
// Change All View to avoid under view display on resize. | ||
// webviewMap.forEach((webview) => { | ||
// webview.hidden = false; | ||
// webview.view.setBounds(fixRect(info.rect, win.isFullScreen())); | ||
// }); | ||
|
||
// Change Single View | ||
const webview = webviewMap.get(info.key); | ||
if (webview) { | ||
webview.hidden = false; | ||
webview.view.setBounds(fixRect(info.rect, win.isFullScreen())); | ||
} | ||
}); | ||
|
||
ipcMain.on('$show-webview', (e, info) => { | ||
log.info('[show-webview] info:', info); | ||
|
||
const webview = webviewMap.get(info.key); | ||
if (webview) { | ||
showWebView(webview); | ||
} | ||
}); | ||
|
||
ipcMain.on('$hide-webview', (e, info) => { | ||
log.info('[hide-webview] info:', info); | ||
|
||
const webview = webviewMap.get(info.key); | ||
if (webview) { | ||
hideWebView(webview); | ||
} | ||
}); | ||
|
||
ipcMain.on('$hide-all-webview', () => { | ||
log.info('[hide-all-webview]'); | ||
|
||
hideAllWebview(); | ||
}); | ||
|
||
ipcMain.on('$clear-all-webview', () => { | ||
if (!win) { | ||
log.info('[clear-all-webview]', 'cannot get mainWindow'); | ||
return; | ||
} | ||
|
||
log.info('[clear-all-webview]'); | ||
|
||
win.getBrowserViews().forEach((view) => { | ||
win.removeBrowserView(view); | ||
}); | ||
|
||
webviewMap.clear(); | ||
}); | ||
} | ||
|
||
const HIDDEN_OFFSET = 3000; | ||
|
||
/** | ||
* Show webview with remove offset in y | ||
*/ | ||
function showWebView(webview: WebviewInfo) { | ||
if (webview.hidden === false) { | ||
return; | ||
} | ||
|
||
webview.hidden = false; | ||
const oldBounds = webview.view.getBounds(); | ||
webview.view.setBounds({ | ||
...oldBounds, | ||
y: oldBounds.y - HIDDEN_OFFSET, | ||
}); | ||
} | ||
|
||
/** | ||
* Hide webview with append offset in y | ||
*/ | ||
function hideWebView(webview: WebviewInfo) { | ||
if (webview.hidden === true) { | ||
return; | ||
} | ||
|
||
webview.hidden = true; | ||
const oldBounds = webview.view.getBounds(); | ||
webview.view.setBounds({ | ||
...oldBounds, | ||
y: oldBounds.y + HIDDEN_OFFSET, | ||
}); | ||
} | ||
|
||
function hideAllWebview() { | ||
Array.from(webviewMap.values()).forEach((webview) => { | ||
hideWebView(webview); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
client/web/plugins/com.msgbyte.env.electron/src/ElectronWebview.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import React, { useEffect, useRef } from 'react'; | ||
|
||
interface ElectronWebviewProps { | ||
className?: string; | ||
src: string; | ||
} | ||
export const ElectronWebview: React.FC<ElectronWebviewProps> = React.memo( | ||
(props) => { | ||
const containerRef = useRef<HTMLDivElement>(null); | ||
const key = props.src; | ||
const url = props.src; | ||
|
||
useEffect(() => { | ||
if (!containerRef.current) { | ||
return; | ||
} | ||
|
||
const rect = containerRef.current.getBoundingClientRect(); | ||
|
||
(window as any).electron.ipcRenderer.sendMessage('$mount-webview', { | ||
key, | ||
url, | ||
rect: { | ||
x: rect.x, | ||
y: rect.y, | ||
width: rect.width, | ||
height: rect.height, | ||
}, | ||
}); | ||
|
||
return () => { | ||
(window as any).electron.ipcRenderer.sendMessage('$unmount-webview', { | ||
key, | ||
}); | ||
}; | ||
}, [key, url]); | ||
|
||
useEffect(() => { | ||
if (!containerRef.current) { | ||
return; | ||
} | ||
|
||
const intersectionObserver = new IntersectionObserver( | ||
(entries) => { | ||
entries.forEach((entry: any) => { | ||
if (entry.isVisible === true) { | ||
// 完全可见,显示 | ||
(window as any).electron.ipcRenderer.sendMessage( | ||
'$show-webview', | ||
{ | ||
key: key, | ||
} | ||
); | ||
} else { | ||
(window as any).electron.ipcRenderer.sendMessage( | ||
'$hide-webview', | ||
{ | ||
key: key, | ||
} | ||
); | ||
} | ||
}); | ||
}, | ||
{ | ||
trackVisibility: true, | ||
delay: 200, | ||
} as any | ||
); | ||
|
||
const resizeObserver = new ResizeObserver((entries) => { | ||
entries.forEach((entry) => { | ||
const { target } = entry; | ||
if (!target.parentElement) { | ||
return; | ||
} | ||
|
||
const rect = target.getBoundingClientRect(); | ||
|
||
(window as any).electron.ipcRenderer.sendMessage( | ||
'$update-webview-rect', | ||
{ | ||
key: key, | ||
rect: { | ||
x: rect.x, | ||
y: rect.y, | ||
width: rect.width, | ||
height: rect.height, | ||
}, | ||
} | ||
); | ||
}); | ||
}); | ||
|
||
intersectionObserver.observe(containerRef.current); | ||
resizeObserver.observe(containerRef.current); | ||
|
||
return () => { | ||
if (containerRef.current) { | ||
intersectionObserver.unobserve(containerRef.current); | ||
resizeObserver.unobserve(containerRef.current); | ||
} | ||
}; | ||
}, [key]); | ||
|
||
return ( | ||
<div | ||
ref={containerRef} | ||
className={props.className} | ||
style={{ width: '100%', height: '100%' }} | ||
/> | ||
); | ||
} | ||
); | ||
ElectronWebview.displayName = 'ElectronWebview'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
client/web/plugins/com.msgbyte.env.electron/src/overwrite.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.ant-dropdown-menu { | ||
box-shadow: none; /* avoid group detail dropdown's shadow will make dom invisiable */ | ||
} |
Oops, something went wrong.