62 lines
2.7 KiB
TypeScript
62 lines
2.7 KiB
TypeScript
import { BrowserWindow, clipboard, Menu } from 'electron'
|
|
import type { IpcMainInvokeEvent, MenuItemConstructorOptions } from 'electron'
|
|
import type { ChatClipboardAction, ChatTextMenuAction, ChatTextMenuOptions } from '../shared/chatTextMenu'
|
|
|
|
function trustedChatFrame(event: IpcMainInvokeEvent): boolean {
|
|
if (event.sender.isDestroyed() || event.senderFrame !== event.sender.mainFrame) return false
|
|
const window = BrowserWindow.fromWebContents(event.sender)
|
|
return Boolean(window && !window.isDestroyed())
|
|
}
|
|
|
|
export function readChatClipboardState(event: IpcMainInvokeEvent): { hasText: boolean } {
|
|
return { hasText: trustedChatFrame(event) && clipboard.readText().length > 0 }
|
|
}
|
|
|
|
export function performChatClipboardAction(event: IpcMainInvokeEvent, input: ChatClipboardAction): boolean {
|
|
if (!trustedChatFrame(event) || !input) return false
|
|
if (input.action === 'copy' && typeof input.text === 'string' && input.text.length > 0 && input.text.length <= 5_000_000) {
|
|
clipboard.writeText(input.text)
|
|
return true
|
|
}
|
|
if (input.action === 'paste' && clipboard.readText().length > 0) {
|
|
event.sender.paste()
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
export function showChatTextMenu(event: IpcMainInvokeEvent, input: ChatTextMenuOptions): Promise<ChatTextMenuAction> | null {
|
|
if (!input || !['message', 'input'].includes(input.kind) || event.sender.isDestroyed() || event.senderFrame !== event.sender.mainFrame) return null
|
|
const window = BrowserWindow.fromWebContents(event.sender)
|
|
if (!window || window.isDestroyed()) return null
|
|
const label = (value: unknown, fallback: string): string => typeof value === 'string' && value.trim() ? value.slice(0, 80) : fallback
|
|
|
|
return new Promise((resolve) => {
|
|
const items: MenuItemConstructorOptions[] = [{
|
|
label: label(input.labels?.copy, 'Copy'),
|
|
enabled: input.canCopy === true,
|
|
click: () => {
|
|
if (!event.sender.isDestroyed() && input.canCopy === true) event.sender.copy()
|
|
resolve(null)
|
|
}
|
|
}]
|
|
if (input.kind === 'input') {
|
|
items.push({
|
|
label: label(input.labels?.paste, 'Paste'),
|
|
enabled: input.canPaste === true && clipboard.readText().length > 0,
|
|
click: () => {
|
|
if (!event.sender.isDestroyed() && input.canPaste === true && clipboard.readText().length > 0) event.sender.paste()
|
|
resolve(null)
|
|
}
|
|
})
|
|
} else if (input.canClear !== undefined) {
|
|
items.push({ type: 'separator' }, {
|
|
label: label(input.labels?.clear, 'Delete conversation'),
|
|
enabled: input.canClear === true,
|
|
click: () => resolve(input.canClear === true ? 'clear' : null)
|
|
})
|
|
}
|
|
Menu.buildFromTemplate(items).popup({ window, callback: () => resolve(null) })
|
|
})
|
|
}
|