mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-25 11:20:07 +08:00
* feat: intercept webview keyboard shortcuts for search functionality Implemented keyboard shortcut interception in webview to enable search functionality (Ctrl/Cmd+F) and navigation (Enter/Escape) within mini app pages. Previously, these shortcuts were consumed by the webview content and not propagated to the host application. Changes: - Added Webview_SearchHotkey IPC channel for forwarding keyboard events - Implemented before-input-event handler in WebviewService to intercept Ctrl/Cmd+F, Escape, and Enter - Extended preload API with onFindShortcut callback for webview shortcut events - Updated WebviewSearch component to handle shortcuts from both window and webview - Added comprehensive test coverage for webview shortcut handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix lint * refactor: improve webview hotkey initialization and error handling Refactored webview keyboard shortcut handler for better code organization and reliability. Changes: - Extracted keyboard handler logic into reusable attachKeyboardHandler function - Added initWebviewHotkeys() to initialize handlers for existing webviews on startup - Integrated initialization in main app entry point - Added explanatory comment for event.preventDefault() behavior - Added warning log when webContentsId is unavailable in WebviewSearch 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: add WebviewKeyEvent type and update related components - Introduced WebviewKeyEvent type to standardize keyboard event handling for webviews. - Updated preload index to utilize the new WebviewKeyEvent type in the onFindShortcut callback. - Refactored WebviewSearch component and its tests to accommodate the new type, enhancing type safety and clarity. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix lint --------- Co-authored-by: Claude <noreply@anthropic.com>
98 lines
2.7 KiB
TypeScript
98 lines
2.7 KiB
TypeScript
import { IpcChannel } from '@shared/IpcChannel'
|
|
import { app, session, shell, webContents } from 'electron'
|
|
|
|
/**
|
|
* init the useragent of the webview session
|
|
* remove the CherryStudio and Electron from the useragent
|
|
*/
|
|
export function initSessionUserAgent() {
|
|
const wvSession = session.fromPartition('persist:webview')
|
|
const originUA = wvSession.getUserAgent()
|
|
const newUA = originUA.replace(/CherryStudio\/\S+\s/, '').replace(/Electron\/\S+\s/, '')
|
|
|
|
wvSession.setUserAgent(newUA)
|
|
wvSession.webRequest.onBeforeSendHeaders((details, cb) => {
|
|
const headers = {
|
|
...details.requestHeaders,
|
|
'User-Agent': details.url.includes('google.com') ? originUA : newUA
|
|
}
|
|
cb({ requestHeaders: headers })
|
|
})
|
|
}
|
|
|
|
/**
|
|
* WebviewService handles the behavior of links opened from webview elements
|
|
* It controls whether links should be opened within the application or in an external browser
|
|
*/
|
|
export function setOpenLinkExternal(webviewId: number, isExternal: boolean) {
|
|
const webview = webContents.fromId(webviewId)
|
|
if (!webview) return
|
|
|
|
webview.setWindowOpenHandler(({ url }) => {
|
|
if (isExternal) {
|
|
shell.openExternal(url)
|
|
return { action: 'deny' }
|
|
} else {
|
|
return { action: 'allow' }
|
|
}
|
|
})
|
|
}
|
|
|
|
const attachKeyboardHandler = (contents: Electron.WebContents) => {
|
|
if (contents.getType?.() !== 'webview') {
|
|
return
|
|
}
|
|
|
|
const handleBeforeInput = (event: Electron.Event, input: Electron.Input) => {
|
|
if (!input) {
|
|
return
|
|
}
|
|
|
|
const key = input.key?.toLowerCase()
|
|
if (!key) {
|
|
return
|
|
}
|
|
|
|
const isFindShortcut = (input.control || input.meta) && key === 'f'
|
|
const isEscape = key === 'escape'
|
|
const isEnter = key === 'enter'
|
|
|
|
if (!isFindShortcut && !isEscape && !isEnter) {
|
|
return
|
|
}
|
|
// Prevent default to override the guest page's native find dialog
|
|
// and keep shortcuts routed to our custom search overlay
|
|
event.preventDefault()
|
|
|
|
const host = contents.hostWebContents
|
|
if (!host || host.isDestroyed()) {
|
|
return
|
|
}
|
|
|
|
host.send(IpcChannel.Webview_SearchHotkey, {
|
|
webviewId: contents.id,
|
|
key,
|
|
control: Boolean(input.control),
|
|
meta: Boolean(input.meta),
|
|
shift: Boolean(input.shift),
|
|
alt: Boolean(input.alt)
|
|
})
|
|
}
|
|
|
|
contents.on('before-input-event', handleBeforeInput)
|
|
contents.once('destroyed', () => {
|
|
contents.removeListener('before-input-event', handleBeforeInput)
|
|
})
|
|
}
|
|
|
|
export function initWebviewHotkeys() {
|
|
webContents.getAllWebContents().forEach((contents) => {
|
|
if (contents.isDestroyed()) return
|
|
attachKeyboardHandler(contents)
|
|
})
|
|
|
|
app.on('web-contents-created', (_, contents) => {
|
|
attachKeyboardHandler(contents)
|
|
})
|
|
}
|