Preserve vector content when pasting from external tools (#9182)

Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
Renzo 2026-04-27 21:35:52 +02:00 committed by GitHub
parent ea265da1f3
commit 8a8ebb7943
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 18 additions and 3 deletions

View File

@ -44,6 +44,8 @@
- Add a search bar to filter colors in the color palette toolbar (by @eureka0928) [Github #7653](https://github.com/penpot/penpot/issues/7653)
- Allow customising the OIDC login button label (by @wdeveloper16) [Github #7027](https://github.com/penpot/penpot/issues/7027)
- Add page separators in Workspace [Taiga #13611](https://tree.taiga.io/project/penpot/us/13611?milestone=262806)
- Preserve vector content when pasting from external tools such as Inkscape: recognise SVG sent as text/plain (with optional XML declaration and HTML comments), skip the raster preview when an SVG sibling is on the clipboard, and ignore empty SVG blobs that some tools advertise alongside the real payload, so pasted graphics arrive editable without spurious "SVG is invalid" warnings [Github #546](https://github.com/penpot/penpot/issues/546)
- Add Shift+Numpad0/1/2 as aliases to Shift+0/1/2 for zoom shortcuts [Github #2457](https://github.com/penpot/penpot/issues/2457)
### :bug: Bugs fixed

View File

@ -24,6 +24,13 @@ const exclusiveTypes = [
"text/plain"
];
const svgTextPattern =
/^(\s*<\?xml[^?]*\?>\s*)?(\s*<!--[\s\S]*?-->\s*)*<svg[\s>]/i;
function hasSvgItem(items) {
return items.some((item) => item?.type === "image/svg+xml");
}
/**
* @typedef {Object} ClipboardSettings
* @property {Function} [decodeTransit]
@ -59,7 +66,7 @@ function parseText(text, options) {
}
}
if (/^<svg[\s>]/i.test(text)) {
if (svgTextPattern.test(text)) {
return new Blob([text], { type: "image/svg+xml" });
} else {
return new Blob([text], { type: "text/plain" });
@ -207,14 +214,20 @@ export async function fromDataTransfer(dataTransfer, options) {
}),
);
return items
.filter((item) => !!item)
.reduce((filtered, item) => {
.filter((item) => !!item && item.size > 0)
.reduce((filtered, item, _index, all) => {
if (
exclusiveTypes.includes(item.type) &&
filtered.find((filteredItem) => exclusiveTypes.includes(filteredItem.type))
) {
return filtered;
}
if (
item.type !== "image/svg+xml" && item.type.startsWith("image/") &&
hasSvgItem(all)
) {
return filtered;
}
filtered.push(item);
return filtered;
}, []);