|
@@ -10,26 +10,34 @@ import { actionDeleteSelected } from "./actionDeleteSelected";
|
|
import { exportCanvas } from "../data/index";
|
|
import { exportCanvas } from "../data/index";
|
|
import { getNonDeletedElements, isTextElement } from "../element";
|
|
import { getNonDeletedElements, isTextElement } from "../element";
|
|
import { t } from "../i18n";
|
|
import { t } from "../i18n";
|
|
|
|
+import { isFirefox } from "../constants";
|
|
|
|
|
|
export const actionCopy = register({
|
|
export const actionCopy = register({
|
|
name: "copy",
|
|
name: "copy",
|
|
trackEvent: { category: "element" },
|
|
trackEvent: { category: "element" },
|
|
- perform: (elements, appState, _, app) => {
|
|
|
|
|
|
+ perform: async (elements, appState, _, app) => {
|
|
const elementsToCopy = app.scene.getSelectedElements({
|
|
const elementsToCopy = app.scene.getSelectedElements({
|
|
selectedElementIds: appState.selectedElementIds,
|
|
selectedElementIds: appState.selectedElementIds,
|
|
includeBoundTextElement: true,
|
|
includeBoundTextElement: true,
|
|
includeElementsInFrames: true,
|
|
includeElementsInFrames: true,
|
|
});
|
|
});
|
|
|
|
|
|
- copyToClipboard(elementsToCopy, app.files);
|
|
|
|
|
|
+ try {
|
|
|
|
+ await copyToClipboard(elementsToCopy, app.files);
|
|
|
|
+ } catch (error: any) {
|
|
|
|
+ return {
|
|
|
|
+ commitToHistory: false,
|
|
|
|
+ appState: {
|
|
|
|
+ ...appState,
|
|
|
|
+ errorMessage: error.message,
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
|
|
return {
|
|
return {
|
|
commitToHistory: false,
|
|
commitToHistory: false,
|
|
};
|
|
};
|
|
},
|
|
},
|
|
- predicate: (elements, appState, appProps, app) => {
|
|
|
|
- return app.device.isMobile && !!navigator.clipboard;
|
|
|
|
- },
|
|
|
|
contextItemLabel: "labels.copy",
|
|
contextItemLabel: "labels.copy",
|
|
// don't supply a shortcut since we handle this conditionally via onCopy event
|
|
// don't supply a shortcut since we handle this conditionally via onCopy event
|
|
keyTest: undefined,
|
|
keyTest: undefined,
|
|
@@ -38,15 +46,91 @@ export const actionCopy = register({
|
|
export const actionPaste = register({
|
|
export const actionPaste = register({
|
|
name: "paste",
|
|
name: "paste",
|
|
trackEvent: { category: "element" },
|
|
trackEvent: { category: "element" },
|
|
- perform: (elements: any, appStates: any, data, app) => {
|
|
|
|
- app.pasteFromClipboard(null);
|
|
|
|
|
|
+ perform: async (elements, appState, data, app) => {
|
|
|
|
+ const MIME_TYPES: Record<string, string> = {};
|
|
|
|
+ try {
|
|
|
|
+ try {
|
|
|
|
+ const clipboardItems = await navigator.clipboard?.read();
|
|
|
|
+ for (const item of clipboardItems) {
|
|
|
|
+ for (const type of item.types) {
|
|
|
|
+ try {
|
|
|
|
+ const blob = await item.getType(type);
|
|
|
|
+ MIME_TYPES[type] = await blob.text();
|
|
|
|
+ } catch (error: any) {
|
|
|
|
+ console.warn(
|
|
|
|
+ `Cannot retrieve ${type} from clipboardItem: ${error.message}`,
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (Object.keys(MIME_TYPES).length === 0) {
|
|
|
|
+ console.warn(
|
|
|
|
+ "No clipboard data found from clipboard.read(). Falling back to clipboard.readText()",
|
|
|
|
+ );
|
|
|
|
+ // throw so we fall back onto clipboard.readText()
|
|
|
|
+ throw new Error("No clipboard data found");
|
|
|
|
+ }
|
|
|
|
+ } catch (error: any) {
|
|
|
|
+ try {
|
|
|
|
+ MIME_TYPES["text/plain"] = await navigator.clipboard?.readText();
|
|
|
|
+ } catch (error: any) {
|
|
|
|
+ console.warn(`Cannot readText() from clipboard: ${error.message}`);
|
|
|
|
+ if (isFirefox) {
|
|
|
|
+ return {
|
|
|
|
+ commitToHistory: false,
|
|
|
|
+ appState: {
|
|
|
|
+ ...appState,
|
|
|
|
+ errorMessage: t("hints.firefox_clipboard_write"),
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ throw error;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } catch (error: any) {
|
|
|
|
+ console.error(`actionPaste: ${error.message}`);
|
|
|
|
+ return {
|
|
|
|
+ commitToHistory: false,
|
|
|
|
+ appState: {
|
|
|
|
+ ...appState,
|
|
|
|
+ errorMessage: error.message,
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ try {
|
|
|
|
+ console.log("actionPaste (1)", { MIME_TYPES });
|
|
|
|
+ const event = new ClipboardEvent("paste", {
|
|
|
|
+ clipboardData: new DataTransfer(),
|
|
|
|
+ });
|
|
|
|
+ for (const [type, value] of Object.entries(MIME_TYPES)) {
|
|
|
|
+ try {
|
|
|
|
+ event.clipboardData?.setData(type, value);
|
|
|
|
+ } catch (error: any) {
|
|
|
|
+ console.warn(
|
|
|
|
+ `Cannot set ${type} as clipboardData item: ${error.message}`,
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ event.clipboardData?.types.forEach((type) => {
|
|
|
|
+ console.log(
|
|
|
|
+ `actionPaste (2) event.clipboardData?.getData(${type})`,
|
|
|
|
+ event.clipboardData?.getData(type),
|
|
|
|
+ );
|
|
|
|
+ });
|
|
|
|
+ app.pasteFromClipboard(event);
|
|
|
|
+ } catch (error: any) {
|
|
|
|
+ return {
|
|
|
|
+ commitToHistory: false,
|
|
|
|
+ appState: {
|
|
|
|
+ ...appState,
|
|
|
|
+ errorMessage: error.message,
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+ }
|
|
return {
|
|
return {
|
|
commitToHistory: false,
|
|
commitToHistory: false,
|
|
};
|
|
};
|
|
},
|
|
},
|
|
- predicate: (elements, appState, appProps, app) => {
|
|
|
|
- return app.device.isMobile && !!navigator.clipboard;
|
|
|
|
- },
|
|
|
|
contextItemLabel: "labels.paste",
|
|
contextItemLabel: "labels.paste",
|
|
// don't supply a shortcut since we handle this conditionally via onCopy event
|
|
// don't supply a shortcut since we handle this conditionally via onCopy event
|
|
keyTest: undefined,
|
|
keyTest: undefined,
|