chore: upgrade Electron to 44.5.1 and adapt clipboard APIs
This commit is contained in:
@@ -148,7 +148,7 @@ async function main() {
|
|||||||
[
|
[
|
||||||
'appId: live.digiman.goodbuddy.harness-smoke',
|
'appId: live.digiman.goodbuddy.harness-smoke',
|
||||||
'productName: GoodBuddyHarnessSmoke',
|
'productName: GoodBuddyHarnessSmoke',
|
||||||
'electronVersion: "43.2.0"',
|
`electronVersion: "${require('electron/package.json').version}"`,
|
||||||
'asar: true',
|
'asar: true',
|
||||||
'npmRebuild: false',
|
'npmRebuild: false',
|
||||||
'files:',
|
'files:',
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Debian `loong64` 桌面,不代表 GoodBuddy 已将龙芯纳入正式发布支
|
|||||||
均为 LoongArch ELF。
|
均为 LoongArch ELF。
|
||||||
|
|
||||||
预览版使用社区维护的 Electron 42.3.0 LoongArch 构建。GoodBuddy 正式版本
|
预览版使用社区维护的 Electron 42.3.0 LoongArch 构建。GoodBuddy 正式版本
|
||||||
当前使用 Electron 43.2.0,因此该预览包只用于移植和真机验证,不能作为正式
|
当前使用 Electron 44.5.1,因此该预览包只用于移植和真机验证,不能作为正式
|
||||||
发布基线。Electron 官方目前也不提供 Linux LoongArch 预编译包。
|
发布基线。Electron 官方目前也不提供 Linux LoongArch 预编译包。
|
||||||
|
|
||||||
已完成的实际构建结果:
|
已完成的实际构建结果:
|
||||||
|
|||||||
Generated
+4
-4
@@ -84,7 +84,7 @@
|
|||||||
"@xterm/addon-search": "0.16.0",
|
"@xterm/addon-search": "0.16.0",
|
||||||
"@xterm/addon-webgl": "0.19.0",
|
"@xterm/addon-webgl": "0.19.0",
|
||||||
"@xterm/xterm": "6.0.0",
|
"@xterm/xterm": "6.0.0",
|
||||||
"electron": "^43.2.0",
|
"electron": "^44.5.1",
|
||||||
"electron-builder": "^26.15.3",
|
"electron-builder": "^26.15.3",
|
||||||
"electron-vite": "^5.0.0",
|
"electron-vite": "^5.0.0",
|
||||||
"esbuild": "0.25.12",
|
"esbuild": "0.25.12",
|
||||||
@@ -14755,9 +14755,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron": {
|
"node_modules/electron": {
|
||||||
"version": "43.2.0",
|
"version": "44.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-43.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-44.5.1.tgz",
|
||||||
"integrity": "sha512-80zvrgG7ZRXD+tD0IyLvrnN9n+veSxadMRsMaC9wKKP3iUbtC7rGM8+dVuCmOb0Rrwwv8ESW4awnUZh9Hbp1fA==",
|
"integrity": "sha512-lx7AYoFIiyvEvgvpyWO9kOC60xtNPpiPZAth6Fmg9NutzpCAMLwpUlaBY2EXfZyIgUdzEmLMj9KvK0Vrokl1yQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -385,7 +385,7 @@
|
|||||||
"@xterm/addon-search": "0.16.0",
|
"@xterm/addon-search": "0.16.0",
|
||||||
"@xterm/addon-webgl": "0.19.0",
|
"@xterm/addon-webgl": "0.19.0",
|
||||||
"@xterm/xterm": "6.0.0",
|
"@xterm/xterm": "6.0.0",
|
||||||
"electron": "^43.2.0",
|
"electron": "^44.5.1",
|
||||||
"electron-builder": "^26.15.3",
|
"electron-builder": "^26.15.3",
|
||||||
"electron-vite": "^5.0.0",
|
"electron-vite": "^5.0.0",
|
||||||
"esbuild": "0.25.12",
|
"esbuild": "0.25.12",
|
||||||
|
|||||||
@@ -4,13 +4,16 @@ import { basename, join } from 'node:path'
|
|||||||
import { strToU8, zipSync } from 'fflate'
|
import { strToU8, zipSync } from 'fflate'
|
||||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
const { createFromBuffer, getSources, showOpenDialog } = vi.hoisted(() => ({
|
const { createFromBuffer, getSources, showOpenDialog, readText, readClipboard } = vi.hoisted(() => ({
|
||||||
|
readText: vi.fn(),
|
||||||
|
readClipboard: vi.fn(),
|
||||||
createFromBuffer: vi.fn(),
|
createFromBuffer: vi.fn(),
|
||||||
getSources: vi.fn(),
|
getSources: vi.fn(),
|
||||||
showOpenDialog: vi.fn()
|
showOpenDialog: vi.fn()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('electron', () => ({
|
vi.mock('electron', () => ({
|
||||||
|
clipboard: { readText, read: readClipboard },
|
||||||
desktopCapturer: {
|
desktopCapturer: {
|
||||||
getSources
|
getSources
|
||||||
},
|
},
|
||||||
@@ -31,6 +34,8 @@ import { defaultDocumentParsingSettings } from './document-parsing-settings-stor
|
|||||||
const temporaryDirectories: string[] = []
|
const temporaryDirectories: string[] = []
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
readText.mockReset()
|
||||||
|
readClipboard.mockReset()
|
||||||
getSources.mockReset()
|
getSources.mockReset()
|
||||||
showOpenDialog.mockReset()
|
showOpenDialog.mockReset()
|
||||||
createFromBuffer.mockReset()
|
createFromBuffer.mockReset()
|
||||||
@@ -42,6 +47,40 @@ afterEach(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('ContextManager', () => {
|
describe('ContextManager', () => {
|
||||||
|
it('awaits clipboard text and prefers it over images', async () => {
|
||||||
|
readText.mockResolvedValue(' clipboard text ')
|
||||||
|
const manager = new ContextManager()
|
||||||
|
const attachment = await manager.readClipboard()
|
||||||
|
expect(attachment.kind).toBe('text')
|
||||||
|
expect(attachment.preview).toContain('clipboard text')
|
||||||
|
expect(readClipboard).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('decodes an asynchronous clipboard PNG when text is empty', async () => {
|
||||||
|
readText.mockResolvedValue('')
|
||||||
|
const data = Buffer.from([0x89, 0x50, 0x4e, 0x47])
|
||||||
|
const getType = vi.fn().mockResolvedValue(new Blob([data]))
|
||||||
|
readClipboard.mockResolvedValue([{ types: ['image/png'], getType }])
|
||||||
|
const image = {
|
||||||
|
isEmpty: () => false,
|
||||||
|
getSize: () => ({ width: 640, height: 480 }),
|
||||||
|
resize: vi.fn(),
|
||||||
|
toJPEG: () => Buffer.from([0xff, 0xd8, 0xff, 0xd9])
|
||||||
|
}
|
||||||
|
image.resize.mockReturnValue(image)
|
||||||
|
createFromBuffer.mockReturnValue(image)
|
||||||
|
const attachment = await new ContextManager().readClipboard()
|
||||||
|
expect(getType).toHaveBeenCalledWith('image/png')
|
||||||
|
expect(createFromBuffer).toHaveBeenCalledWith(data)
|
||||||
|
expect(attachment.kind).toBe('image')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rejects a clipboard without text or a supported image', async () => {
|
||||||
|
readText.mockResolvedValue('')
|
||||||
|
readClipboard.mockResolvedValue([{ types: ['text/html'] }])
|
||||||
|
await expect(new ContextManager().readClipboard()).rejects.toThrow('剪贴板中没有可用的文本或图片')
|
||||||
|
})
|
||||||
|
|
||||||
it('preserves logical attachment identity and frozen message bytes across a draft reparse', async () => {
|
it('preserves logical attachment identity and frozen message bytes across a draft reparse', async () => {
|
||||||
const directory = await mkdtemp(join(tmpdir(), 'goodbuddy-reparse-'))
|
const directory = await mkdtemp(join(tmpdir(), 'goodbuddy-reparse-'))
|
||||||
temporaryDirectories.push(directory)
|
temporaryDirectories.push(directory)
|
||||||
|
|||||||
@@ -568,15 +568,19 @@ export class ContextManager {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
readClipboard(): ContextAttachment {
|
async readClipboard(): Promise<ContextAttachment> {
|
||||||
const text = clipboard.readText().trim()
|
const text = (await clipboard.readText()).trim()
|
||||||
if (text) {
|
if (text) {
|
||||||
return this.storeText('剪贴板文本.txt', text)
|
return this.storeText('剪贴板文本.txt', text)
|
||||||
}
|
}
|
||||||
const image = clipboard.readImage()
|
for (const item of await clipboard.read()) {
|
||||||
|
if (!item.types.includes('image/png')) continue
|
||||||
|
const blob = await item.getType('image/png')
|
||||||
|
const image = nativeImage.createFromBuffer(Buffer.from(await blob.arrayBuffer()))
|
||||||
if (!image.isEmpty()) {
|
if (!image.isEmpty()) {
|
||||||
return this.storeImage('剪贴板图片.png', image)
|
return this.storeImage('剪贴板图片.png', image)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
throw new Error('剪贴板中没有可用的文本或图片')
|
throw new Error('剪贴板中没有可用的文本或图片')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -264,9 +264,9 @@ describe('terminal IPC boundary', () => {
|
|||||||
snapshot.sessionId
|
snapshot.sessionId
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
await expect(
|
||||||
electronMocks.handlers.get(ipcChannels.clipboardReadText)?.(event)
|
electronMocks.handlers.get(ipcChannels.clipboardReadText)?.(event)
|
||||||
).toBe('Get-Location\r')
|
).resolves.toBe('Get-Location\r')
|
||||||
expect(electronMocks.readClipboardText).toHaveBeenCalledOnce()
|
expect(electronMocks.readClipboardText).toHaveBeenCalledOnce()
|
||||||
|
|
||||||
electronMocks.handlers.get(ipcChannels.clipboardWriteText)?.(
|
electronMocks.handlers.get(ipcChannels.clipboardWriteText)?.(
|
||||||
@@ -353,8 +353,8 @@ const electronMocks = vi.hoisted(() => {
|
|||||||
openPath: vi.fn(async () => ''),
|
openPath: vi.fn(async () => ''),
|
||||||
showItemInFolder: vi.fn(),
|
showItemInFolder: vi.fn(),
|
||||||
openExternal: vi.fn(async () => undefined),
|
openExternal: vi.fn(async () => undefined),
|
||||||
readClipboardText: vi.fn(() => 'Get-Location\r'),
|
readClipboardText: vi.fn(async () => 'Get-Location\r'),
|
||||||
writeClipboardText: vi.fn()
|
writeClipboardText: vi.fn(async () => {})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ export function registerClipboardIpcHandlers(
|
|||||||
): void {
|
): void {
|
||||||
registerHandler(ipcChannels.clipboardReadText, (event) => {
|
registerHandler(ipcChannels.clipboardReadText, (event) => {
|
||||||
assertTrustedSender(event, window)
|
assertTrustedSender(event, window)
|
||||||
return clipboardTextSchema.parse(clipboard.readText())
|
return clipboard.readText().then((text) => clipboardTextSchema.parse(text))
|
||||||
})
|
})
|
||||||
|
|
||||||
registerHandler(ipcChannels.clipboardWriteText, (event, input) => {
|
registerHandler(ipcChannels.clipboardWriteText, (event, input) => {
|
||||||
assertTrustedSender(event, window)
|
assertTrustedSender(event, window)
|
||||||
clipboard.writeText(clipboardTextSchema.parse(input))
|
return clipboard.writeText(clipboardTextSchema.parse(input))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user