Merge pull request #211 from joetam/fix-image-paste-stubs
fix linux clipboard image paste for jpeg/gif/webp
This commit is contained in:
@@ -34,6 +34,17 @@ type SharpCreator = (options: SharpCreatorOptions) => SharpInstance
|
|||||||
let imageProcessorModule: { default: SharpFunction } | null = null
|
let imageProcessorModule: { default: SharpFunction } | null = null
|
||||||
let imageCreatorModule: { default: SharpCreator } | null = null
|
let imageCreatorModule: { default: SharpCreator } | null = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error thrown when no image processor is available (e.g., in the open build
|
||||||
|
* where sharp and image-processor-napi are stubbed out).
|
||||||
|
*/
|
||||||
|
export class ImageProcessorUnavailableError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super('No image processor available (sharp is not installed)')
|
||||||
|
this.name = 'ImageProcessorUnavailableError'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function getImageProcessor(): Promise<SharpFunction> {
|
export async function getImageProcessor(): Promise<SharpFunction> {
|
||||||
if (imageProcessorModule) {
|
if (imageProcessorModule) {
|
||||||
return imageProcessorModule.default
|
return imageProcessorModule.default
|
||||||
@@ -44,10 +55,14 @@ export async function getImageProcessor(): Promise<SharpFunction> {
|
|||||||
try {
|
try {
|
||||||
// Use the native image processor module
|
// Use the native image processor module
|
||||||
const imageProcessor = await import('image-processor-napi')
|
const imageProcessor = await import('image-processor-napi')
|
||||||
|
if ((imageProcessor as { __stub?: boolean }).__stub) {
|
||||||
|
throw new ImageProcessorUnavailableError()
|
||||||
|
}
|
||||||
const sharp = imageProcessor.sharp || imageProcessor.default
|
const sharp = imageProcessor.sharp || imageProcessor.default
|
||||||
imageProcessorModule = { default: sharp }
|
imageProcessorModule = { default: sharp }
|
||||||
return sharp
|
return sharp
|
||||||
} catch {
|
} catch (e) {
|
||||||
|
if (e instanceof ImageProcessorUnavailableError) throw e
|
||||||
// Fall back to sharp if native module is not available
|
// Fall back to sharp if native module is not available
|
||||||
// biome-ignore lint/suspicious/noConsole: intentional warning
|
// biome-ignore lint/suspicious/noConsole: intentional warning
|
||||||
console.warn(
|
console.warn(
|
||||||
@@ -58,12 +73,20 @@ export async function getImageProcessor(): Promise<SharpFunction> {
|
|||||||
|
|
||||||
// Use sharp for non-bundled builds or as fallback.
|
// Use sharp for non-bundled builds or as fallback.
|
||||||
// Single structural cast: our SharpFunction is a subset of sharp's actual type surface.
|
// Single structural cast: our SharpFunction is a subset of sharp's actual type surface.
|
||||||
|
try {
|
||||||
const imported = (await import(
|
const imported = (await import(
|
||||||
'sharp'
|
'sharp'
|
||||||
)) as unknown as MaybeDefault<SharpFunction>
|
)) as unknown as MaybeDefault<SharpFunction> & { __stub?: boolean }
|
||||||
const sharp = unwrapDefault(imported)
|
if (imported && (imported as { __stub?: boolean }).__stub) {
|
||||||
|
throw new ImageProcessorUnavailableError()
|
||||||
|
}
|
||||||
|
const sharp = unwrapDefault(imported as MaybeDefault<SharpFunction>)
|
||||||
imageProcessorModule = { default: sharp }
|
imageProcessorModule = { default: sharp }
|
||||||
return sharp
|
return sharp
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof ImageProcessorUnavailableError) throw e
|
||||||
|
throw new ImageProcessorUnavailableError()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,6 +28,31 @@ type SupportedPlatform = 'darwin' | 'linux' | 'win32'
|
|||||||
|
|
||||||
// Threshold in characters for when to consider text a "large paste"
|
// Threshold in characters for when to consider text a "large paste"
|
||||||
export const PASTE_THRESHOLD = 800
|
export const PASTE_THRESHOLD = 800
|
||||||
|
|
||||||
|
export const LINUX_CLIPBOARD_IMAGE_MIME_TYPES = [
|
||||||
|
'image/png',
|
||||||
|
'image/jpeg',
|
||||||
|
'image/jpg',
|
||||||
|
'image/gif',
|
||||||
|
'image/webp',
|
||||||
|
'image/bmp',
|
||||||
|
]
|
||||||
|
|
||||||
|
export function buildLinuxClipboardCheckCommand(): string {
|
||||||
|
const mimePattern = LINUX_CLIPBOARD_IMAGE_MIME_TYPES.map(mimeType =>
|
||||||
|
mimeType.replace('/', '\\/'),
|
||||||
|
).join('|')
|
||||||
|
|
||||||
|
return `xclip -selection clipboard -t TARGETS -o 2>/dev/null | grep -E "${mimePattern}" || wl-paste -l 2>/dev/null | grep -E "${mimePattern}"`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildLinuxClipboardSaveCommand(screenshotPath: string): string {
|
||||||
|
return LINUX_CLIPBOARD_IMAGE_MIME_TYPES.flatMap(mimeType => [
|
||||||
|
`xclip -selection clipboard -t ${mimeType} -o > "${screenshotPath}" 2>/dev/null`,
|
||||||
|
`wl-paste --type ${mimeType} > "${screenshotPath}" 2>/dev/null`,
|
||||||
|
]).join(' || ')
|
||||||
|
}
|
||||||
|
|
||||||
function getClipboardCommands() {
|
function getClipboardCommands() {
|
||||||
const platform = process.platform as SupportedPlatform
|
const platform = process.platform as SupportedPlatform
|
||||||
|
|
||||||
@@ -62,9 +87,8 @@ function getClipboardCommands() {
|
|||||||
deleteFile: `rm -f "${screenshotPath}"`,
|
deleteFile: `rm -f "${screenshotPath}"`,
|
||||||
},
|
},
|
||||||
linux: {
|
linux: {
|
||||||
checkImage:
|
checkImage: buildLinuxClipboardCheckCommand(),
|
||||||
'xclip -selection clipboard -t TARGETS -o 2>/dev/null | grep -E "image/(png|jpeg|jpg|gif|webp|bmp)" || wl-paste -l 2>/dev/null | grep -E "image/(png|jpeg|jpg|gif|webp|bmp)"',
|
saveImage: buildLinuxClipboardSaveCommand(screenshotPath),
|
||||||
saveImage: `xclip -selection clipboard -t image/png -o > "${screenshotPath}" 2>/dev/null || wl-paste --type image/png > "${screenshotPath}" 2>/dev/null || xclip -selection clipboard -t image/bmp -o > "${screenshotPath}" 2>/dev/null || wl-paste --type image/bmp > "${screenshotPath}"`,
|
|
||||||
getPath:
|
getPath:
|
||||||
'xclip -selection clipboard -t text/plain -o 2>/dev/null || wl-paste 2>/dev/null',
|
'xclip -selection clipboard -t text/plain -o 2>/dev/null || wl-paste 2>/dev/null',
|
||||||
deleteFile: `rm -f "${screenshotPath}"`,
|
deleteFile: `rm -f "${screenshotPath}"`,
|
||||||
|
|||||||
Reference in New Issue
Block a user