1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-04-08 11:04:42 +02:00

Compare commits

..

4 Commits

Author SHA1 Message Date
Laurent Cozic
737a494db8 update 2026-04-08 09:44:23 +01:00
Laurent Cozic
b8bfe85f21 update 2026-04-05 19:05:44 +01:00
Laurent Cozic
379a53eca5 Revert "Desktop: Fixes #14613: Fix JPEG image paste from clipboard on Linux (#14750)"
This reverts commit 05fc3e9104.

Ref: https://github.com/laurent22/joplin/issues/15022#issuecomment-4189250496
2026-04-05 18:48:56 +01:00
renovate[bot]
548d1a49ba Update dependency @types/uuid to v11 (#15018)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-05 18:46:19 +01:00
8 changed files with 45 additions and 97 deletions

View File

@@ -11,6 +11,7 @@ const baseContext: Record<string, any> = {
noteIsMarkdown: true,
noteIsReadOnly: false,
richTextEditorVisible: false,
hasActivePluginEditor: false,
};
describe('editorCommandDeclarations', () => {

View File

@@ -22,10 +22,10 @@ export const enabledCondition = (commandName: string) => {
const allowInViewerAndReadOnlyMode = worksInViewerAndReadOnlyMode.includes(commandName);
const editorPaneCondition = markdownEditorOnly
? 'markdownEditorPaneVisible'
? '(markdownEditorPaneVisible || hasActivePluginEditor)'
: allowInViewerAndReadOnlyMode
? '(markdownEditorPaneVisible || richTextEditorVisible || markdownViewerPaneVisible)'
: '(markdownEditorPaneVisible || richTextEditorVisible)';
? '(markdownEditorPaneVisible || richTextEditorVisible || markdownViewerPaneVisible || hasActivePluginEditor)'
: '(markdownEditorPaneVisible || richTextEditorVisible || hasActivePluginEditor)';
const output = [
// gotoAnythingVisible: Enable if the command palette (which is a modal dialog) is visible

View File

@@ -1,24 +1,10 @@
import Setting from '@joplin/lib/models/Setting';
import { processImagesInPastedHtml, processPastedHtml, getResourcesFromPasteEvent } from './resourceHandling';
import { processImagesInPastedHtml, processPastedHtml } from './resourceHandling';
import markupLanguageUtils from '@joplin/lib/markupLanguageUtils';
import HtmlToMd from '@joplin/lib/HtmlToMd';
import { HtmlToMarkdownHandler, MarkupToHtmlHandler } from './types';
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
jest.mock('electron', () => ({
clipboard: {
has: jest.fn(),
readBuffer: jest.fn(),
},
}));
interface ClipboardMock {
has: jest.Mock;
readBuffer: jest.Mock;
}
const mockClipboard = (require('electron') as { clipboard: ClipboardMock }).clipboard;
const createTestMarkupConverters = () => {
const markupToHtml: MarkupToHtmlHandler = async (markupLanguage, markup, options) => {
const conv = markupLanguageUtils.newMarkupToHtml({}, {
@@ -37,11 +23,6 @@ const createTestMarkupConverters = () => {
};
describe('resourceHandling', () => {
afterEach(() => {
mockClipboard.has.mockReset();
mockClipboard.readBuffer.mockReset();
});
it('should sanitize pasted HTML', async () => {
Setting.setConstant('resourceDir', '/home/.config/joplin/resources');
@@ -148,39 +129,4 @@ describe('resourceHandling', () => {
expect(result).not.toContain(expectAbsent);
expect(result).not.toContain('data:');
});
// Tests for getResourcesFromPasteEvent - clipboard image paste (issue #14613)
// The test environment (non-Electron, no sharp) skips image validation and
// just copies the file, so any non-empty buffer works as test data.
const testImageBuffer = Buffer.from(minimalPng, 'base64');
test.each([
{ format: 'image/jpeg', description: 'JPEG (bug #14613)' },
{ format: 'image/jpg', description: 'JPG alias' },
{ format: 'image/png', description: 'PNG (regression check)' },
])('should paste $description image from clipboard via getResourcesFromPasteEvent', async ({ format }) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
mockClipboard.has.mockImplementation((f: string) => f === format);
mockClipboard.readBuffer.mockImplementation((f: string) => {
return f === format ? testImageBuffer : Buffer.alloc(0);
});
const mockEvent = { preventDefault: jest.fn() };
const result = await getResourcesFromPasteEvent(mockEvent);
expect(result.length).toBe(1);
expect(result[0]).toContain('](:/');
expect(mockEvent.preventDefault).toHaveBeenCalledTimes(1);
});
test.each([
{ description: 'clipboard has no image', hasResult: false },
{ description: 'buffer is empty despite has() returning true', hasResult: true },
])('should return empty when $description', async ({ hasResult }) => {
mockClipboard.has.mockReturnValue(hasResult);
mockClipboard.readBuffer.mockReturnValue(Buffer.alloc(0));
const mockEvent = { preventDefault: jest.fn() };
const result = await getResourcesFromPasteEvent(mockEvent);
expect(result).toEqual([]);
expect(mockEvent.preventDefault).not.toHaveBeenCalled();
});
});

View File

@@ -93,38 +93,28 @@ export function resourcesStatus(resourceInfos: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
export async function getResourcesFromPasteEvent(event: any) {
const output = [];
const formats = clipboard.availableFormats();
for (let i = 0; i < formats.length; i++) {
const format = formats[i].toLowerCase();
const formatType = format.split('/')[0];
// clipboard.has() and readBuffer() are used instead of availableFormats() and
// readImage(), which don't work for JPEG on Linux.
// https://github.com/laurent22/joplin/issues/14613
const supportedFormats = ['image/png', 'image/jpeg', 'image/jpg'];
for (const format of supportedFormats) {
if (!clipboard.has(format)) continue;
const data = clipboard.readBuffer(format);
if (!data || data.length === 0) continue;
if (event) event.preventDefault();
const fileExt = mimeUtils.toFileExtension(format);
const filePath = `${Setting.value('tempDir')}/${md5(Date.now() + Math.random())}.${fileExt}`;
let md = null;
try {
await shim.fsDriver().writeFile(filePath, data, 'buffer');
md = await commandAttachFileToBody('', [filePath]);
} finally {
try {
await shim.fsDriver().remove(filePath);
} catch (cleanupError) {
logger.warn('getResourcesFromPasteEvent: Failed to remove temporary file.', cleanupError);
if (formatType === 'image') {
// writeImageToFile can process only image/jpeg, image/jpg or image/png mime types
if (['image/png', 'image/jpg', 'image/jpeg'].indexOf(format) < 0) {
continue;
}
}
if (event) event.preventDefault();
if (md) {
output.push(md);
break;
const image = clipboard.readImage();
const fileExt = mimeUtils.toFileExtension(format);
const filePath = `${Setting.value('tempDir')}/${md5(Date.now())}.${fileExt}`;
await shim.writeImageToFile(image, format, filePath);
const md = await commandAttachFileToBody('', [filePath]);
await shim.fsDriver().remove(filePath);
if (md) output.push(md);
}
}
return output;

View File

@@ -30,7 +30,7 @@
"@types/node-rsa": "1.1.4",
"@types/react": "19.1.10",
"@types/react-dom": "19.1.7",
"@types/uuid": "10.0.0",
"@types/uuid": "11.0.0",
"jest": "29.7.0",
"jest-expect-message": "1.1.3",
"jsdom": "26.1.0",

View File

@@ -30,7 +30,7 @@
"@joplin/utils": "~3.6",
"@koa/cors": "3.4.3",
"@types/qrcode": "1.5.6",
"@types/uuid": "10.0.0",
"@types/uuid": "11.0.0",
"bcryptjs": "2.4.3",
"bulma": "1.0.4",
"compare-versions": "6.1.1",

View File

@@ -33,7 +33,7 @@
"@types/jest-expect-message": "1.1.0",
"@types/koa": "2.15.0",
"@types/sharp": "0.32.0",
"@types/uuid": "10.0.0",
"@types/uuid": "11.0.0",
"gulp": "4.0.2",
"jest": "29.7.0",
"jest-expect-message": "1.1.3",

View File

@@ -11073,7 +11073,7 @@ __metadata:
"@types/node-rsa": "npm:1.1.4"
"@types/react": "npm:19.1.10"
"@types/react-dom": "npm:19.1.7"
"@types/uuid": "npm:10.0.0"
"@types/uuid": "npm:11.0.0"
adm-zip: "npm:0.5.16"
async-mutex: "npm:0.5.0"
base-64: "npm:1.0.0"
@@ -11297,7 +11297,7 @@ __metadata:
"@types/node-os-utils": "npm:1.3.4"
"@types/nodemailer": "npm:6.4.21"
"@types/qrcode": "npm:1.5.6"
"@types/uuid": "npm:10.0.0"
"@types/uuid": "npm:11.0.0"
"@types/yargs": "npm:17.0.35"
"@types/zxcvbn": "npm:4.4.5"
bcryptjs: "npm:2.4.3"
@@ -11404,7 +11404,7 @@ __metadata:
"@types/jest-expect-message": "npm:1.1.0"
"@types/koa": "npm:2.15.0"
"@types/sharp": "npm:0.32.0"
"@types/uuid": "npm:10.0.0"
"@types/uuid": "npm:11.0.0"
dotenv: "npm:17.2.3"
file-type: "npm:16.5.4"
fs-extra: "npm:11.3.3"
@@ -17546,10 +17546,12 @@ __metadata:
languageName: node
linkType: hard
"@types/uuid@npm:10.0.0":
version: 10.0.0
resolution: "@types/uuid@npm:10.0.0"
checksum: 10/e3958f8b0fe551c86c14431f5940c3470127293280830684154b91dc7eb3514aeb79fe3216968833cf79d4d1c67f580f054b5be2cd562bebf4f728913e73e944
"@types/uuid@npm:11.0.0":
version: 11.0.0
resolution: "@types/uuid@npm:11.0.0"
dependencies:
uuid: "npm:*"
checksum: 10/9f94bd34e5d220c53cc58ea9f48a0061d3bc343e29bc33a17edc705f5e21fedda21553318151f2bc227c2b2b03727bbb536da2b82a61f84d2e1ca38abc5e5c3f
languageName: node
linkType: hard
@@ -52880,6 +52882,15 @@ __metadata:
languageName: node
linkType: hard
"uuid@npm:*":
version: 13.0.0
resolution: "uuid@npm:13.0.0"
bin:
uuid: dist-node/bin/uuid
checksum: 10/2742b24d1e00257e60612572e4d28679423469998cafbaf1fe9f1482e3edf9c40754b31bfdb3d08d71b29239f227a304588f75210b3b48f2609f0673f1feccef
languageName: node
linkType: hard
"uuid@npm:11.1.0, uuid@npm:^11.1.0":
version: 11.1.0
resolution: "uuid@npm:11.1.0"