feat: 新增包文件

This commit is contained in:
duanshuwen
2025-11-15 22:41:50 +08:00
parent 7b65193e5c
commit 7ada85f175
104 changed files with 11273 additions and 1 deletions

View File

@@ -0,0 +1,249 @@
import { app, BrowserWindow, clipboard, Menu, MenuItem } from 'electron'
const LABELS = {
openInNewTab: (type: 'link' | Electron.ContextMenuParams['mediaType']) =>
`Open ${type} in new tab`,
openInNewWindow: (type: 'link' | Electron.ContextMenuParams['mediaType']) =>
`Open ${type} in new window`,
copyAddress: (type: 'link' | Electron.ContextMenuParams['mediaType']) => `Copy ${type} address`,
undo: 'Undo',
redo: 'Redo',
cut: 'Cut',
copy: 'Copy',
delete: 'Delete',
paste: 'Paste',
selectAll: 'Select All',
back: 'Back',
forward: 'Forward',
reload: 'Reload',
inspect: 'Inspect',
addToDictionary: 'Add to dictionary',
exitFullScreen: 'Exit full screen',
emoji: 'Emoji',
}
const getBrowserWindowFromWebContents = (webContents: Electron.WebContents) => {
return BrowserWindow.getAllWindows().find((win) => {
if (win.webContents === webContents) return true
let browserViews: Electron.BrowserView[]
if ('getBrowserViews' in win) {
browserViews = win.getBrowserViews()
} else if ('getBrowserView' in win) {
// @ts-ignore
browserViews = [win.getBrowserView()]
} else {
browserViews = []
}
return browserViews.some((view) => view.webContents === webContents)
})
}
type ChromeContextMenuLabels = typeof LABELS
interface ChromeContextMenuOptions {
/** Context menu parameters emitted from the WebContents 'context-menu' event. */
params: Electron.ContextMenuParams
/** WebContents which emitted the 'context-menu' event. */
webContents: Electron.WebContents
/** Handler for opening links. */
openLink: (
url: string,
disposition: 'default' | 'foreground-tab' | 'background-tab' | 'new-window',
params: Electron.ContextMenuParams,
) => void
/** Chrome extension menu items. */
extensionMenuItems?: MenuItem[]
/** Labels used to create menu items. Replace this if localization is needed. */
labels?: ChromeContextMenuLabels
/**
* @deprecated Use 'labels' instead.
*/
strings?: ChromeContextMenuLabels
}
export const buildChromeContextMenu = (opts: ChromeContextMenuOptions): Menu => {
const { params, webContents, openLink, extensionMenuItems } = opts
const labels = opts.labels || opts.strings || LABELS
const menu = new Menu()
const append = (opts: Electron.MenuItemConstructorOptions) => menu.append(new MenuItem(opts))
const appendSeparator = () => menu.append(new MenuItem({ type: 'separator' }))
if (params.linkURL) {
append({
label: labels.openInNewTab('link'),
click: () => {
openLink(params.linkURL, 'default', params)
},
})
append({
label: labels.openInNewWindow('link'),
click: () => {
openLink(params.linkURL, 'new-window', params)
},
})
appendSeparator()
append({
label: labels.copyAddress('link'),
click: () => {
clipboard.writeText(params.linkURL)
},
})
appendSeparator()
} else if (params.mediaType !== 'none') {
// TODO: Loop, Show controls
append({
label: labels.openInNewTab(params.mediaType),
click: () => {
openLink(params.srcURL, 'default', params)
},
})
append({
label: labels.copyAddress(params.mediaType),
click: () => {
clipboard.writeText(params.srcURL)
},
})
appendSeparator()
}
if (params.isEditable) {
if (params.misspelledWord) {
for (const suggestion of params.dictionarySuggestions) {
append({
label: suggestion,
click: () => webContents.replaceMisspelling(suggestion),
})
}
if (params.dictionarySuggestions.length > 0) appendSeparator()
append({
label: labels.addToDictionary,
click: () => webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord),
})
} else {
if (
app.isEmojiPanelSupported() &&
!['input-number', 'input-telephone'].includes(params.formControlType)
) {
append({
label: labels.emoji,
click: () => app.showEmojiPanel(),
})
appendSeparator()
}
append({
label: labels.redo,
enabled: params.editFlags.canRedo,
click: () => webContents.redo(),
})
append({
label: labels.undo,
enabled: params.editFlags.canUndo,
click: () => webContents.undo(),
})
}
appendSeparator()
append({
label: labels.cut,
enabled: params.editFlags.canCut,
click: () => webContents.cut(),
})
append({
label: labels.copy,
enabled: params.editFlags.canCopy,
click: () => webContents.copy(),
})
append({
label: labels.paste,
enabled: params.editFlags.canPaste,
click: () => webContents.paste(),
})
append({
label: labels.delete,
enabled: params.editFlags.canDelete,
click: () => webContents.delete(),
})
appendSeparator()
if (params.editFlags.canSelectAll) {
append({
label: labels.selectAll,
click: () => webContents.selectAll(),
})
appendSeparator()
}
} else if (params.selectionText) {
append({
label: labels.copy,
click: () => {
clipboard.writeText(params.selectionText)
},
})
appendSeparator()
}
if (menu.items.length === 0) {
const browserWindow = getBrowserWindowFromWebContents(webContents)
// TODO: Electron needs a way to detect whether we're in HTML5 full screen.
// Also need to properly exit full screen in Blink rather than just exiting
// the Electron BrowserWindow.
if (browserWindow?.fullScreen) {
append({
label: labels.exitFullScreen,
click: () => browserWindow.setFullScreen(false),
})
appendSeparator()
}
append({
label: labels.back,
enabled: webContents.navigationHistory.canGoBack(),
click: () => webContents.navigationHistory.goBack(),
})
append({
label: labels.forward,
enabled: webContents.navigationHistory.canGoForward(),
click: () => webContents.navigationHistory.goForward(),
})
append({
label: labels.reload,
click: () => webContents.reload(),
})
appendSeparator()
}
if (extensionMenuItems) {
extensionMenuItems.forEach((item) => menu.append(item))
if (extensionMenuItems.length > 0) appendSeparator()
}
append({
label: labels.inspect,
click: () => {
webContents.inspectElement(params.x, params.y)
if (!webContents.isDevToolsFocused()) {
webContents.devToolsWebContents?.focus()
}
},
})
return menu
}
export default buildChromeContextMenu