diff --git a/skyvern-frontend/src/components/ui/hidden-copyable-input.tsx b/skyvern-frontend/src/components/ui/hidden-copyable-input.tsx
index d8e1dda3..b7df5fc1 100644
--- a/skyvern-frontend/src/components/ui/hidden-copyable-input.tsx
+++ b/skyvern-frontend/src/components/ui/hidden-copyable-input.tsx
@@ -2,6 +2,7 @@ import { useState } from "react";
import { Button } from "./button";
import { Input } from "./input";
import { CheckIcon, CopyIcon } from "@radix-ui/react-icons";
+import { copyText } from "@/util/copyText";
type Props = {
value: string;
@@ -22,14 +23,15 @@ function HiddenCopyableInput({ value }: Props) {
size="sm"
variant="secondary"
className="cursor-pointer"
- onClick={async () => {
+ onClick={() => {
if (hidden) {
setHidden(false);
return;
}
- await navigator.clipboard.writeText(value);
- setCopied(true);
- setTimeout(() => setCopied(false), 3000);
+ copyText(value).then(() => {
+ setCopied(true);
+ setTimeout(() => setCopied(false), 3000);
+ });
}}
>
{!hidden && !copied && }
diff --git a/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx b/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx
index 69010562..60707486 100644
--- a/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx
+++ b/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx
@@ -47,6 +47,7 @@ import {
import { OrganizationApiResponse } from "@/api/types";
import { Skeleton } from "@/components/ui/skeleton";
import { MAX_STEPS_DEFAULT } from "../constants";
+import { copyText } from "@/util/copyText";
const createNewTaskFormSchema = z
.object({
@@ -487,10 +488,11 @@ function CreateNewTaskForm({ initialValues }: Props) {
"x-api-key": apiCredential ?? "",
},
});
- await navigator.clipboard.writeText(curl);
- toast({
- title: "Copied cURL",
- description: "cURL copied to clipboard",
+ copyText(curl).then(() => {
+ toast({
+ title: "Copied cURL",
+ description: "cURL copied to clipboard",
+ });
});
}}
>
diff --git a/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx b/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx
index 469051f1..9bd65e43 100644
--- a/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx
+++ b/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx
@@ -49,6 +49,7 @@ import {
import { OrganizationApiResponse } from "@/api/types";
import { MAX_STEPS_DEFAULT } from "../constants";
import { Skeleton } from "@/components/ui/skeleton";
+import { copyText } from "@/util/copyText";
const savedTaskFormSchema = z
.object({
@@ -629,10 +630,11 @@ function SavedTaskForm({ initialValues }: Props) {
"x-api-key": apiCredential ?? "",
},
});
- await navigator.clipboard.writeText(curl);
- toast({
- title: "Copied cURL",
- description: "cURL copied to clipboard",
+ copyText(curl).then(() => {
+ toast({
+ title: "Copied cURL",
+ description: "cURL copied to clipboard",
+ });
});
}}
>
diff --git a/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx b/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx
index ae7c1422..864faf4d 100644
--- a/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx
+++ b/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx
@@ -26,6 +26,7 @@ import { taskIsFinalized } from "@/api/utils";
import fetchToCurl from "fetch-to-curl";
import { apiBaseUrl } from "@/util/env";
import { useApiCredential } from "@/hooks/useApiCredential";
+import { copyText } from "@/util/copyText";
function createTaskRequestObject(values: TaskApiResponse) {
return {
@@ -142,12 +143,13 @@ function TaskDetails() {
"x-api-key": apiCredential ?? "",
},
});
- navigator.clipboard.writeText(curl);
- toast({
- variant: "success",
- title: "Copied to Clipboard",
- description:
- "The cURL command has been copied to your clipboard.",
+ copyText(curl).then(() => {
+ toast({
+ variant: "success",
+ title: "Copied to Clipboard",
+ description:
+ "The cURL command has been copied to your clipboard.",
+ });
});
}}
>
diff --git a/skyvern-frontend/src/util/copyText.ts b/skyvern-frontend/src/util/copyText.ts
new file mode 100644
index 00000000..16795e58
--- /dev/null
+++ b/skyvern-frontend/src/util/copyText.ts
@@ -0,0 +1,25 @@
+/**
+ * Progressively enhanced text copying
+ * https://web.dev/patterns/clipboard/copy-text
+ */
+async function copyText(text: string): Promise {
+ if ("clipboard" in navigator) {
+ return navigator.clipboard.writeText(text);
+ } else {
+ const textArea = document.createElement("textarea");
+ textArea.value = text;
+ textArea.style.opacity = "0";
+ document.body.appendChild(textArea);
+ textArea.focus();
+ textArea.select();
+ const success = document.execCommand("copy");
+ document.body.removeChild(textArea);
+ if (success) {
+ return Promise.resolve();
+ } else {
+ return Promise.reject();
+ }
+ }
+}
+
+export { copyText };