You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-04-08 11:04:42 +02:00
Compare commits
4 Commits
ios-v13.6.
...
fix_paste
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
737a494db8 | ||
|
|
b8bfe85f21 | ||
|
|
379a53eca5 | ||
|
|
548d1a49ba |
@@ -11,6 +11,7 @@ const baseContext: Record<string, any> = {
|
||||
noteIsMarkdown: true,
|
||||
noteIsReadOnly: false,
|
||||
richTextEditorVisible: false,
|
||||
hasActivePluginEditor: false,
|
||||
};
|
||||
|
||||
describe('editorCommandDeclarations', () => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
25
yarn.lock
25
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user